宽度优先搜索 之 CODE[VS] 1004 四子连棋
/*
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 }