題目鏈接: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;
}