/*
bfs + hash判重
第一次接触“hash判重”(哈希函数是依据于取余),是一种很好的思想,不过也有小的瑕疵:
hash判重:
棋盘表示:空(0),白(1),黑(2)
整个棋盘一共16个格子,可以看成3进制的16位数,将其转化为10进制数,找一个质数取余,利用余数的不同来给棋盘的状态判重。
(用质数取余的原因:我不知道为什么用质数取余,但是《算法导论》以及其他地方都推荐用质数取余,应该不会错的。。。)
细心一下就会发现,这里面有问题:
定义用于取余的质数为y,需要取余的数集为x[],已知: y < max(x[])
所以会存在这种情况:x[1]%y == x[2]%y (x[1] != x[2])
故:判重结果出现错误。。。
这种担心是对的,但是测试数据出现这种情况的概率很小,所以题目可以AC。
另,为进一步降低冲突的概率,斌神推荐:可以用两个质数进行取模,两个质数同时取模都相等几乎不可能了
(我看了一些资料,发现用于取模的质数还是有很多讲究的,但是暂时先这么取吧。。。)
对于质数的选取,个人的看法:
尽量在x[]的最大值附近取(在保证不爆内存的情况下),这样冲突的概率应该更低一些。
*/
1 #include <iostream>
2 #include <cstdlib>
3 #include <cstdio>
4 #include <cstddef>
5 #include <iterator>
6 #include <algorithm>
7 #include <string>
8 #include <locale>
9 #include <cmath>
10 #include <vector>
11 #include <cstring>
12 #include <map>
13 #include <queue>
14 #include <stack>
15 #include <set>
16 using namespace std;
17 const int INF = -0x3f3f3f3f;
18 const int MaxN = 10;
19 const int modPrime = 3046721; // hash判重时,取余时用的质数,由void getPrime()函数获得。
20
21 int step[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
22
23 struct Node
24 {
25 int stepNum; // 走的步数
26 int chessboard[MaxN][MaxN]; // 棋盘状态:空(0),白(1),黑(2)
27 int now; // 当前走的棋子是白(1),是黑(2),若是第一次出发(0)
28 Node()
29 {
30 stepNum = 0;
31 now = 0;
32 }
33 int blank[2][2]; // 棋盘空的位置
34 };
35
36 Node startOff;
37 bool hashJudge[modPrime];
38
39 // 4个数同时相等,返回true;否则返回false
40 bool equMpl(const int i1, const int i2, const int i3 , const int i4)
41 {
42 if ((i1 != i2) || (i2 != i3) || (i3 != i4) || (i4 != i1))
43 {
44 return false;
45 }
46 return true;
47 }
48
49 // 判断是否为目标棋局
50 bool check(const int chessboard[][MaxN])
51 {
52
53 for (int i = 0; i < 4; ++i)
54 {
55 if (equMpl(chessboard[i][0], chessboard[i][1], chessboard[i][2], chessboard[i][3]))
56 {
57 return true;
58 }
59 if (equMpl(chessboard[0][i], chessboard[1][i], chessboard[2][i], chessboard[3][i]))
60 {
61 return true;
62 }
63 }
64 if (equMpl(chessboard[0][0], chessboard[1][1], chessboard[2][2], chessboard[3][3]))
65 {
66 return true;
67 }
68 if (equMpl(chessboard[0][3], chessboard[1][2], chessboard[2][1], chessboard[3][0]))
69 {
70 return true;
71 }
72 return false;
73 }
74
75 // hash判重,判断棋局是否已经出现过
76 bool isUsed(const int chessboard[][MaxN])
77 {
78 int sum = 0;
79 int tmp = 1;
80 for (int i = 0; i < 4; ++i)
81 {
82 for (int j = 0; j < 4; ++j)
83 {
84 sum += (tmp*chessboard[i][j]);
85 tmp *= 3;
86 }
87 }
88 sum %= modPrime;
89 if (hashJudge[sum])
90 {
91 return true;
92 }
93 hashJudge[sum] = true;
94 return false;
95 }
96
97 // 输出棋局的状态
98 void output(const int chessboard[][MaxN])
99 {
100 for (int i = 0; i < 4; ++i)
101 {
102 for (int j = 0; j < 4; ++j)
103 {
104 if (chessboard[i][j] == 0)
105 {
106 cout << 'O';
107 continue;
108 }
109 if (chessboard[i][j] == 1)
110 {
111 cout << 'W';
112 continue;
113 }
114 cout << 'B';
115 }
116 cout << endl;
117 }
118 }
119
120 void Solve()
121 {
122 queue<Node> que;
123 que.push(startOff);
124 while (!que.empty())
125 {
126 const Node node = que.front();
127 que.pop();
128 if (check(node.chessboard))
129 {
130 cout << node.stepNum << endl;
131 //output(node.chessboard);
132 break;
133 }
134
135 for (int i = 0; i < 2; ++i)
136 {
137 for (int j = 0; j < 4; ++j)
138 {
139 int x = node.blank[i][0] + step[j][0];
140 int y = node.blank[i][1] + step[j][1];
141 if ((x >= 0) && (x <= 3) && (y >= 0) && (y <= 3) && (node.chessboard[x][y] != node.now))
142 {
143 Node nodeTmp;
144 for (int i = 0; i < 4; ++i)
145 {
146 for (int j = 0; j < 4; ++j)
147 {
148 nodeTmp.chessboard[i][j] = node.chessboard[i][j];
149 }
150 }
151 nodeTmp.chessboard[node.blank[i][0]][node.blank[i][1]] = node.chessboard[x][y];
152 nodeTmp.chessboard[x][y] = 0;
153 nodeTmp.stepNum = node.stepNum + 1;
154 nodeTmp.now = node.chessboard[x][y];
155 nodeTmp.blank[0][0] = node.blank[(i + 1) % 2][0];
156 nodeTmp.blank[0][1] = node.blank[(i + 1) % 2][1];
157 nodeTmp.blank[1][0] = x;
158 nodeTmp.blank[1][1] = y;
159
160 if (!isUsed(nodeTmp.chessboard))
161 {
162 que.push(nodeTmp);
163 }
164 }
165 }
166 }
167 }
168 }
169
170
171 // 找到小于10000000(10^7)的最大质数,作为取余时,用的质数
172 void getPrime()
173 {
174 // 获得棋盘状态最大值(16位全是2的情况)
175 int maxNum = 1, tmp = 1;
176 for (int i = 0; i < 16; ++i)
177 {
178 maxNum += (2 * tmp);
179 tmp *= 3;
180 }
181 cout << "棋盘状态最大值: " << maxNum << endl;
182 maxNum %= 10000000;
183 int cnt = 0;
184 for (int i = maxNum; i >= 5; --i)
185 {
186 bool isPrime = true;
187 for (int j = 2; j <= sqrt(i); ++j)
188 {
189 if (i%j == 0)
190 {
191 isPrime = false;
192 break;
193 }
194 }
195 if (isPrime)
196 {
197 cout << "取余时,用的质数:"<< i << endl;
198 break;
199 }
200 }
201 }
202
203
204 int main()
205 {
206 #ifdef HOME
207 freopen("in", "r", stdin);
208 //freopen("out", "w", stdout);
209 #endif
210
211 //getPrime();
212 memset(hashJudge, 0, sizeof(hashJudge));
213 string str;
214 int blankPos = 0;
215 for (int i = 0; i < 4; ++i)
216 {
217 cin >> str;
218 for (int j = 0; j < 4; ++j)
219 {
220
221 if (str[j] == 'B')
222 {
223 startOff.chessboard[i][j] = 2;
224 continue;
225 }
226 if (str[j] == 'W')
227 {
228 startOff.chessboard[i][j] = 1;
229 continue;
230 }
231 startOff.chessboard[i][j] = 0;
232 startOff.blank[blankPos][0] = i;
233 startOff.blank[blankPos][1] = j;
234 ++blankPos;
235 }
236 }
237 Solve();
238
239
240 #ifdef HOME
241 cerr << "Time elapsed: " << clock() / CLOCKS_PER_SEC << " ms" << endl;
242 _CrtDumpMemoryLeaks();
243 #endif
244 return 0;
245 }