poj1753 Flip Game 廣搜哈希

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章