宽度优先搜索 之 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 1;
 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 }
时间: 2024-10-24 04:56:54

宽度优先搜索 之 CODE[VS] 1004 四子连棋的相关文章

Code[VS] 1004 四子连棋

[题意] 给定4*4的棋盘,每个位置上为"B"."W"或" ",表示黑棋.白棋或空格.定义目标棋局为有一行.一列或一条对角线上有相同颜色的四个子.黑白交替下,开始时任意一方先下,求最少多少步能达到目标棋局. [分析] 首先定义变量: 基本   q(1)wt[2][2]记录空格位置 (2)int p[4][4]记录值,黑棋存1,白棋存2,空格存0 哈希表 hash(1)long long data,存棋局的值,就是转化为10进制后存储 (2)nex

1004 四子连棋

1004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 In

Codevs 1004 四子连棋

1004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input

1004 四子连棋 未完成

1004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○  

codevs 1004 四子连棋 BFS、hash判重

004 四子连棋 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input

宽度优先搜索 之 CODE[VS] 1026 逃跑的拉尔夫

/* 读懂题意,bfs即可AC. 不过注意“超出空间 Memory Limit Exceeded”,需要记录节点的状态,判重. bool isUsed[MaxN][MaxN][1010]; // 用于判重 */ 1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstddef> 5 #include <iterator> 6 #include &l

【日常学习】【迭代加深搜索+哈希】codevs1004 四子连棋题解

转载请注明出处 [ametake版权所有]http://blog.csdn.net/ametake 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input

CODEVS——T 1004 四子连棋

http://codevs.cn/problem/1004/ 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ●

CODEVS 1004四子连棋

题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局. ● ○ ●   ○ ● ○ ● ● ○ ● ○ ○ ● ○   输入描述 Input Description 从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用