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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值