POJ 1753

来源:互联网 发布:淘宝刷销量平台可靠吗 编辑:程序博客网 时间:2024/06/06 03:24

描述

4*4的方格棋盘,每个棋子有白面和黑面,每个方格中的棋子为白/黑,每轮翻3~5个棋子改变它们的颜色,每轮根据以下规则选择翻面的棋子:
1. 选择16个棋子中的任一个
2. 翻转已选棋子的邻近棋子(左、右、上、下,四个邻近的棋子,如果有就翻)
示例:

这里写图片描述

bwbbwwbwbwwwwwbb

b为黑,w为白,若翻第三行第一个,则棋盘变成:
bbwwwwwwbwwwwwbb

目标是把所有棋子全都白面向上或全都黑面向上,求达成目标的最少回合。

输入

4行,每行4个字符,”w”或”b”,表示给定的棋盘当前状态

输出

从棋盘当前状态达成目标所需的最少游戏回合,若当前已达成目标,输出0,若不可能实现,则输出”Impossible”(不输出引号)

样例输入

bwwb
bbwb
bwwb
bwww

样例输出

4

参考

(1)bfs:http://changgongxiaorong.cn/?p=23
(2)dfs:http://blog.csdn.net/freezhanacmore/article/details/8142014
(3)位运算:http://www.it610.com/article/1987880.htm
根据参考1、参考2用广度搜索和深度搜索都可。

c++实现

(1)广度搜索版

参考1的思路:

广度搜索+枚举+位运算
一个棋子有两种情况,16个棋子,即棋盘有2^16方种情况,即65536种情况,正好一个短整形,所以想到用一个短整形记录棋盘情况,16个位对应棋盘16格,0和1表示黑和白。当状态为0或65535时表示说明全是‘b’或‘w’.
广度搜索使用非递归模式,速度快,用队列实现,元素短整形。全部遍历一遍,剪枝掉重复搜索的情况。

这里写图片描述

代码:
532K 16MS

#include <iostream>using namespace std;const int MAX = 65535;//[0, 65535]共65536种情况unsigned short myqueue[MAX];//用‘位’保存状态的数组int myrear = 0, mytop = 0;// 头尾指针bool flag[MAX];//是否出现过int num[MAX];//记录翻拍次数/**初始化,w为0,b为1,拼接成16位数字*/void init(){    unsigned short temp = 0;    char c;    for (int i = 0; i < 16; ++i)    {        cin >> c;        //1左移i位,为当前黑色位置,temp为现有棋盘,temp不断与新数字按位与,生成倒序的棋盘        if(c == 'b'){            temp |= (1 << i);        }    }    myqueue[myrear++] = temp; //追加新的状态    flag[temp] = true; //该状态已经有过}/**now为当前状态,i为要翻转的棋子序号[0,15]*/unsigned short change(unsigned now, int i){    unsigned short temp = 0;    temp |= (1 << i); //temp为当前要改变颜色的位置    // 翻转相邻的四个棋子    if((i+1)%4 != 0){ //该棋子不是最右边        temp |= (1 << (i+1));    }    if(i%4 != 0){ // 不是最左边        temp |= (1 << (i-1));    }    if(i > 3){ // 不是最上边        temp |= (1 << (i-4));    }    if(i < 12){ // 不是最上面        temp |= (1 << (i+4));    }    return now ^ temp; // 旧状态和新状态相同为0,不同为1}bool bfs(){    // 遍历现有的状态    while(myrear > mytop){        // 前一个状态        unsigned short former = myqueue[mytop++];        for (int i = 0; i < 16; ++i)        {            unsigned short temp = change(former, i);            if(former == 0 || former == 65535){                cout << num[former]; // 全黑或全白了                return true;            }            else if (!flag[temp]) //状态未出现过,剪枝            {                myqueue[myrear++] = temp;                flag[temp] = true;                num[temp] = num[former] + 1; // 每个记录的是当前状态是总共经历了多少次翻牌次数            }        }    }    return false;}int main(){    init();    if(!bfs()){        cout << "Impossible";    }    return 0;}

(2)深度搜索版

参考2的思路:
每次遇到下一个方格有两种处理方法,要么翻转,要么不翻转。
若不翻转方格,则直接处理下一个方格,翻转的方格数不再增加,
若翻转当前方格,则还要翻转相邻的四个方格,然后再处理下一个方格,翻转的总数+1。

0 0