题目链接:http://poj.org/problem?id=1753
题目大意:
这是一个4 * 4的棋盘游戏,每个棋子都有黑白两个颜色,每次可以选择一个棋子进行翻转。当选择一个棋子后,将它本身与它的上下左右(如果有棋子)都翻转过来,例如:
翻转第3行第3个棋子后棋盘状态为:
题目给出一个棋盘的状态,问将棋盘中的棋子全部一个颜色向上最少需要几步?
解题思路:
这道题的思路是用广搜来枚举所有翻转可能的情况,将每次翻转后的棋盘状态哈希成一个数字,记录这种情况是否访问过。利用广搜的思想,记录每次翻转后的哈希值和翻转需要的步数,直到哈希值等于0或65535。
哈希实际上就是一种映射,这道题就是将棋盘的翻转情况转换成一个数字。转换的规则是:将棋盘中的棋子变成0,1组合的数字,黑色('b')代表1,白色('w')代表0。将这串01数字连成一串16位的二进制数,将这个二进制数转换为十进制,所得的十进制数就是响应的哈希值。
比如:
左图的棋盘状态为:变成01数字:这串二进制数 转换为十进制就是:51094所以哈希值就是51094。
棋盘中最多可能产生有 = 65536 种情况,所以哈希值的范围就是0~65535。就可以用一个bool型数组来记录某种情况是否被访问过。在搜索的过程中,每次产生要翻转后的哈希值,记录翻转的次数。当搜索到哈希值是0或65535时(代表所有棋子一个颜色向上),就结束循环。
这道题还可以用位运算符来完成,代码量会更少些。
代码:
/*
ID: Code-Cola
PROG: 1753
LANG: C++
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define MAX 1e9 + 7
using namespace std;
/********** 全局变量 **********/
struct node
{
int h;
int n;
};
bool k[65536]; //记录是否访问的数组
queue<node> q;
/********** 函数声明 **********/
int hash(char ch[4][5]); //产生初始状态的hash
int Nexthash(int h, int x, int y); //产生下一次翻转的hash
int main()
{
int i,j,n,min;
char ch[4][5];
node temp,t;
while (~scanf("%s",ch[0])) {
for (i = 1; i < 4; i++) {
scanf("%s",ch[i]);
}
min = MAX;
memset(k, 1, sizeof(k));
k[hash(ch)] = false; //标记初始状态已访问
temp.h = hash(ch), temp.n = 0;
q.push(temp);
while (!q.empty()) { //广搜
temp = q.front();
q.pop();
if (temp.h == 0 || temp.h == 65535) { //找到目标状态
min = temp.n;
break;
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
t.h = Nexthash(temp.h, j, i);
if (k[t.h]) {
k[t.h] = false; //先标记 后入队
t.n = temp.n + 1; //记录步数
q.push(t);
}
}
}
}
min == MAX ? printf("Impossible\n") : printf("%d\n",min);
}
return 0;
}
int hash(char ch[4][5])
{
int i,j,k,sum;
for (i = 3, sum = 0, k = 1; i >= 0; i--) { //产生初始hash
for (j = 3; j >= 0; j--) {
sum += k * (ch[i][j] == 'b' ? 1 : 0);
k <<= 1;
}
}
return sum;
}
int Nexthash(int h, int x, int y) //当前状态的hash 和要翻转的位置
{
int i,j,k,sum,t;
for (i = 0, sum = 0, k = 32768; i < 4; i++) {
for (j = 0; j < 4; j++) {
t = h % 2;
if ((j == x && i == y) || //模拟翻转 调换01的值
(j == x - 1 && i == y) ||
(j == x + 1 && i == y) ||
(j == x && i == y - 1) ||
(j == x && i == y + 1))
sum += !t * k;
else
sum += t * k; //生成新的hash
h >>= 1;
k >>= 1;
}
}
return sum;
}