0%

算法竞赛入门经典(第2版) 6-9UVa127 - Accordian Patience

题意:52张牌排一行,一旦出现任何一张牌与它左边的第一张或第三张“匹配”,即花色或点数相同,则须立即将其移动到那张牌上面,将其覆盖。能执行以上移动的只有压在最上面的牌。直到最后没有牌能向左移动。 注意细则:如果同时有多张牌都可以移动,你应该采取的策略是移动最左边可移动的牌。当一张牌既可以移动到左边第一张,又可以移动到左边第三张时,应移动到左边第三张上面。


代码:(Accepted,0.100s)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//UVa127 - "Accordian" Patience
//Accepted 0.100s
//#define _XIENAOBAN_
#include<iostream>
#include<cstdio>
#define NUM 52
#define BIAS 1
using namespace std;

struct card {
char a, b;
bool operator ==(card& that) {
return a == that.a || b == that.b;
}

};

struct stack {
card st[NUM + 2], *p;
void ini() {
p = st + 1;
}
card& pop() {
if (p != st) return *(p--);
return *p;
}
card& top() {
return *p;
}
void push(card& c) {
*++p = c;
}
int size() {
return p - st;
}
bool empty() {
return p == st;
}
}cards[NUM + 3];

struct list {
stack *ls[NUM + 2];
list() {
for (int i(0);i < NUM + 1;++i)
ls[i] = cards + i;
ls[NUM + 1] = nullptr;
}
void erease(int n) {
while (ls[n] != nullptr) {
ls[n] = ls[n + 1];
++n;
}
}
stack& operator [](int i) {
if (i >= 0) return *ls[i];
return *ls[0];
}
};


int main()
{
#ifdef _XIENAOBAN_
#define gets(T) gets_s(T, 129)
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif

cards[0].st[1].a = cards[0].st[1].b = 'X'; // 第0个stack的top里储存一个没用的card,使得没有一张卡片与之相等,以方便以后处理前两张左边没牌的牌;
while ((cards[1].st[1].a = getchar()) != '#') {
cards[1].st[1].b = getchar();
getchar();
for (int i(0);i < NUM + 3;++i) cards[i].ini(); // 每个stack里将都只有一张卡片,这个ini就是把top的指针指向1
for (int i(2);i < NUM + 1;++i) { // 数据全部直接作为top读取到stack里面
cards[i].top().a = getchar();
cards[i].top().b = getchar();
getchar();
}
list pile; //处理部分,直接模拟,懒得另写函数了
int num;
for (num = 2;&pile[num];) { // num从2开始,毕竟第一个数据怎么样也移动不了(接下来num再跳回1就懒得管他了)
if (pile[num].top() == pile[num - 3].top()) {
pile[num - 3].push(pile[num].pop());
if (pile[num].empty()) pile.erease(num);
num -= 3;
}
else if (pile[num].top() == pile[num - 1].top()) {
pile[num - 1].push(pile[num].pop());
if (pile[num].empty()) pile.erease(num);
--num;
}
else ++num;
}
printf("%d pile", num - 1);
if (num != 2) printf("s");
printf(" remaining:");
for (int i(1);i < num;++i) printf(" %d", pile[i].size());
puts("");
}
return 0;
}


分析:差不多又是一遍过,而且只用了0.1s,竟然挤进了UVa本题Ranking的前20,虽然题目确实很简单就是了,还是开心的不行。 我的思路是安放52个栈,直接进行模拟操作。从第二张牌开始(毕竟第一张牌不可能向左移动)往下判断,直到到达末尾。 具体流程是,若当前牌可以左移,移之,并且下一个进行判断的牌还是它(因为左侧的都是已经完成的不可能再有可移动的牌了,所以无需重新从最左侧开始检索);反之此牌没法动了,则判断下一张牌。 感觉这不能叫遍历,毕竟期间会向左移动牌,应该算是迭代吧。 要注意的是输出格式!如下图所示,

6 piles remaining: 40 8 1 1 1 1 1 pile remaining: 52

pile/piles的单复数问题。