UVa 1008 - A Vexing Problem

contents

  1. 1. Problem
  2. 2. Sample Input
  3. 3. Sample Output
  4. 4. Solution

Problem

給予一個 Vexed! 遊戲,這遊戲類似於消球遊戲,可以左右拖動特定元素使其掉落,在一個瞬間中可以讓相鄰兩個相同元素同時消失。在下一個瞬間,仍在盤面上的元素會根據重力往下墜落,如果又發現存有相同元素相鄰,則會不斷地迭代消去。

請問如何在最少步數下把所有元素消去。

Sample Input

1
2
3
4
5
6
7
8
9
10
11
12
13
4 5 SAMPLE-01
#A--#
##-B#
#AB##
#####
6 7 SAMPLE-02
#--Y--#
#-ZX-X#
#-##-##
#-XZ--#
####YZ#
#######
0 0 END

Sample Output

1
2
3
4
5
6
SAMPLE-01: Minimum solution length = 2
(B,1,3,L) (A,0,1,R)
SAMPLE-02: Minimum solution length = 9
(Y,0,3,R) (Z,4,5,L) (X,1,3,R) (Z,1,2,R)
(Z,1,3,R) (X,3,4,R) (X,3,2,R) (X,4,5,L)
(X,1,5,L)

Solution

對於每一個盤面做紀錄,並且對於每一個元素嘗試是否能向左向右拉動,拉動之後將其模擬墜落相消的過程。使用 bfs 的方式進行路徑搜索,隨後回溯即可。

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <stdio.h>
#include <string.h>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;
#define hash_mod 1000003
int n, m, visited[16][16], cases = 0;
struct State {
char g[10][10];
int label;
bool operator<(const State &x) const {
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] != x.g[i][j])
return g[i][j] < x.g[i][j];
return false;
}
unsigned int hash() {
unsigned int a = 63689, b = 378551;
unsigned int value = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
value = value * a + g[i][j];
a *= b;
}
}
return value % hash_mod;
}
int dfs(int x, int y, char c) {
if (x < 0 || y < 0 || x >= n || y >= m)
return 0;
if (g[x][y] != c || visited[x][y] == cases)
return 0;
visited[x][y] = cases;
int ret = 0;
ret += dfs(x+1, y, c);
ret += dfs(x, y+1, c);
ret += dfs(x-1, y, c);
ret += dfs(x, y-1, c);
return 1 + ret;
}
void erase(int x, int y, int c) {
if (x < 0 || y < 0 || x >= n || y >= m)
return ;
if (g[x][y] != c || visited[x][y] != cases)
return ;
g[x][y] = '-';
erase(x+1, y, c);
erase(x, y+1, c);
erase(x-1, y, c);
erase(x, y-1, c);
}
void fall() {
for (int i = n-1; i >= 0; i--) {
for (int j = 0; j < m; j++) {
if (g[i][j] >= 'A' && g[i][j] <= 'Z') {
int k = i+1;
while (k < n && g[k][j] == '-') {
g[k][j] = g[k-1][j];
g[k-1][j] = '-';
k++;
}
}
}
}
}
void refresh() {
int update = 0, cnt;
do {
fall();
update = 0;
memset(visited, 0, sizeof(visited));
cases = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (visited[i][j] == 0 && g[i][j] >= 'A' && g[i][j] <= 'Z') {
cases++;
cnt = dfs(i, j, g[i][j]);
if (cnt > 1)
erase(i, j, g[i][j]), update = 1;
}
}
}
} while (update);
}
int isCompleted() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++)
if (g[i][j] >= 'A' && g[i][j] <= 'Z')
return 0;
}
return 1;
}
};
struct Info {
int x, y, dir, prev;
char c;
Info(int s1 = 0, int s2 = 0, int s3 = 0, int s4 = 0, char s = 0):
x(s1), y(s2), dir(s3), prev(s4), c(s) {
}
};
map<State, int> R[hash_mod];
vector<Info> history;
int main() {
char casesName[105];
State st, next;
int h, step;
while (scanf("%d %d %s", &n, &m, casesName) == 3 && n) {
for (int i = 0; i < n; i++)
scanf("%s", &st.g[i]);
history.resize(1);
for (int i = 0; i < hash_mod; i++)
R[i].clear();
int labelCnt = 0, label;
queue<State> Q;
st.refresh(), st.label = 0;
h = st.hash();
R[h][st] = 0, Q.push(st);
while (!Q.empty()) {
st = Q.front(), Q.pop();
h = st.hash(), label = st.label;
step = R[h][st];
if (st.isCompleted())
break;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (st.g[i][j] >= 'A' && st.g[i][j] <= 'Z') {
if (j+1 < m && st.g[i][j+1] == '-') {
next = st;
swap(next.g[i][j], next.g[i][j+1]);
next.refresh();
h = next.hash();
if (R[h].find(next) == R[h].end()) {
next.label = ++labelCnt;
history.push_back(Info(i, j, 1, label, st.g[i][j]));
R[h][next] = step + 1;
Q.push(next);
}
}
if (j-1 >= 0 && st.g[i][j-1] == '-') {
next = st;
swap(next.g[i][j], next.g[i][j-1]);
next.refresh();
h = next.hash();
if (R[h].find(next) == R[h].end()) {
next.label = ++labelCnt;
history.push_back(Info(i, j, 0, label, st.g[i][j]));
R[h][next] = step + 1;
Q.push(next);
}
}
}
}
}
}
printf("%s: Minimum solution length = %d\n", casesName, step);
int idx = st.label;
vector<Info> ret;
while (idx) {
ret.push_back(history[idx]);
idx = history[idx].prev;
}
for (int i = ret.size() - 1, j = 0; i >= 0; i--, j++) {
if (j%4) putchar(' ');
printf("(%c,%d,%d,%c)", ret[i].c, ret[i].x, ret[i].y, ret[i].dir ? 'R' : 'L');
if (j%4 == 3) puts("");
}
if (ret.size()%4) puts("");
}
return 0;
}