题目:http://poj.org/problem?id=1753
题目的意思是:一个棋盘游戏,棋盘由4x4的方格组成,棋子有两种状态,一种是白色,一种是黑色。游戏的规则如下:
1. 棋子可以翻转,每次翻转,原来白色的状态变成黑色,原来黑色的状态变成白色。
2. 翻转任何一个棋子,它的4个相邻(上下左右)的棋子也同时翻转。
当棋盘上16个棋子都变成白色或者黑色,则游戏结束。
具体的输入输出要求可以查看题目,在此就不详细叙述了。
分析这个题目的基本思路如下:
从题目中可以获得以下几条信息:
1. 任何棋子最多翻转一次,翻转超过一次都是没有意义的。(可以证明。简单的说就是翻转两次相当于没有翻转)
2. 棋子只有16个,根据第一条信息每个棋子最多翻一次,那么所有棋子最多翻转16次。
3. 棋子翻转的顺序对最后的结果没有影响。
4. 每个棋子只有两个状态。
根据以上信息可以得到解题的基本思路。由于翻转棋的方案最多只有2^16种,用枚举的方式可以暴力求解。具体的方法可以采用深度优先搜索(DFS)或者广度优先搜索(BFS)。由于题目没有要求输出具体需要翻转哪些棋子,用BFS会比DFS更快的找到最短路径,如果需要输出路径,那么用DFS会更方便。
标示棋盘的数据结构可以采用4x4的二维数组,也可以用16bit来存储棋盘数据。使用bit位来存储数据不但可以节省空间,在进行翻转操作的时候,直接使用异或操作比较简便。
使用DFS方法的代码如下:
#include<stdio.h>
const int status[16]={0xC800 , 0xE400 , 0x7200 , 0x3100 ,
0x8C80 , 0x4E40 , 0x2720 , 0x1310 ,
0x08c8 , 0x04E4 , 0x0272 , 0x0131 ,
0x008c , 0x004E , 0x0027 , 0x0013};
int flipGame=0;
int result=17;
void read()
{
int i;
char c;
for(i=0;i<16;i++)
{
c=getchar();
if(c=='b')
{
flipGame=flipGame<<1;
flipGame^=1;
}
else if(c=='w')
{
flipGame=flipGame<<1;
}
else
{
i--;
}
}
}
void search(int step, int index)
{
if(step>=result)
{
return;
}
if(index>=16)
{
if(flipGame==0xffff || flipGame==0x0000)
{
if(result>step)
{
result=step;
}
}
return;
}
search(step,index+1); //不翻转,进入下一步
flipGame^=status[index];
search(step+1,index+1); //翻转,进入下一步
flipGame^=status[index]; //恢复原位
return;
}
int main(void)
{
read();
search(0,0);
if (result >= 17)
{
printf("Impossible");
}
else
printf("%d",result);
printf("\n");
return 1;
}
BFS要使用到队列,为了方便,使用了C++的标准库。BFS方法的代码如下:
#include<cstdio>
#include<queue>
using namespace std;
const int status[16]={0xC800 , 0xE400 , 0x7200 , 0x3100 ,
0x8C80 , 0x4E40 , 0x2720 , 0x1310 ,
0x08c8 , 0x04E4 , 0x0272 , 0x0131 ,
0x008c , 0x004E , 0x0027 , 0x0013};
int flipGame=0;
int result;
typedef struct Node
{
int state;
int step;
}flipNode;
void read()
{
int i;
char c;
for(i=0;i<16;i++)
{
c=getchar();
if(c=='b')
{
flipGame=flipGame<<1;
flipGame^=1;
}
else if(c=='w')
{
flipGame=flipGame<<1;
}
else
{
i--;
}
}
}
int bfs(int flipGame)
{
int i;
bool vis[65536];
memset(vis , 0, sizeof(vis));
queue<flipNode>node;
flipNode front,next;
front.state=flipGame;
front.step=0;
node.push(front);
vis[flipGame]=true;
while(!node.empty())
{
front=node.front();
node.pop();
if(front.state == 0 || front.state == 0xffff )
{
return front.step;
}
for(i=0;i<16;i++)
{
next.state=front.state^status[i];
next.step=front.step+1;
if(vis[next.state])
continue;
if(next.state == 0 || next.state == 0xffff)
return next.step;
vis[next.state] = true;
node.push(next);
}
}
return -1;
}
int main(void)
{
read();
result=bfs(flipGame);
if (result ==-1)
{
printf("Impossible\n");
}
else
printf("%d\n",result);
printf("\n");
return 0;
}
使用DFS方法输出翻转路径的代码如下:
#include<stdio.h>
const int status[16]={0xC800 , 0xE400 , 0x7200 , 0x3100 ,
0x8C80 , 0x4E40 , 0x2720 , 0x1310 ,
0x08c8 , 0x04E4 , 0x0272 , 0x0131 ,
0x008c , 0x004E , 0x0027 , 0x0013};
int flipGame=0;
int result=17;
int path=0; //记录路径
void read()
{
int i;
char c;
for(i=0;i<16;i++)
{
c=getchar();
if(c=='b')
{
flipGame=flipGame<<1;
flipGame^=1;
}
else if(c=='w')
{
flipGame=flipGame<<1;
}
else
{
i--;
}
}
}
void search(int step, int index, int p)
{
if(step>=result)
{
return;
}
if(index>=16)
{
if(flipGame==0xffff || flipGame==0x0000)
{
if(result>step)
{
result=step;
path=p;
}
}
return;
}
p=p<<1;
search(step,index+1,p);
p=p>>1;
flipGame^=status[index];
p=p<<1;
p^=1;
search(step+1,index+1,p);
flipGame^=status[index]; //恢复原位
p=p>>1;
return;
}
int main(void)
{
int t,i,j;
read();
search(0,0,0);
if (result >= 17)
{
printf("Impossible\n");
}
else
printf("%d\n",result);
for(i=4;i>=1;i--)
for(j=4;j>=1;j--)
{
t=path%2;
path=path>>1;
if(t)
{
printf("(%d,%d)",i,j);
}
}
printf("\n");
return 1;
}