您的位置:首页 > 其它

POJ 1753

2016-03-02 21:19 246 查看

描述

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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj