题目链接:http://poj.org/problem?id=1753
题目大意:
翻棋子游戏,给定一个4*4的棋盘上摆放的初始状态,棋子有黑白两色,求将所有棋子翻成同一色的最少步数。规则:每次最多翻转一个棋子和其上下左右共五个棋子,则最少三个,因为四个角上的棋子在边界上,有两个邻居不存在。若无法翻转成同一个颜色则输出 "IImpossible",初始状态即为目标状态时输出0。
题目思路:
棋子总共只有两种颜色,则可用0或1表示当前状态,共十六颗棋子则总共只有2^16=65536种状态。
则我们可以用一个hash判重,将当前状态一个十六位的二进制数转换成十进制数标记。解决了状态标记,判重问题,接下来就只需一个基础的BFS即可!
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 70000
int queue[MAX];
int step[MAX]; //记录走到当前状态的最少步数
int a[16];
int n,m;
void find(int x) //将一个十进制数转换成二进制数
{
int i=16,k=0;
while (i--)
{
a[k]=x%2;
x=x/2;
k++;
}
}
int check() //将二进制数转换成十进制数
{
int s=a[0],temp=1,i;
for (i=1;i<16;i++)
{
temp*=2;
s+=a[i]*temp;
}
return s;
}
void bfs()
{
int qe=0,qs=0,i,u;
queue[qe++]=n; //初始状态入队
step[n]=0;
while (qs<qe)
{
int temp=queue[qs++];
if (temp==0||temp==65535)
{
m=step[temp];
break;
}
find(temp);
for (i=0;i<16;i++) //每次最多有十六个状态可扩展
{
int s,x,z,y;
s=i-4;
x=i+4;
z=i-1;
y=i+1;
a[i]=(a[i]+1)%2;
if (s>=0&&s<16) //判断与当前棋子相邻的上方的棋子能否翻转
a[s]=(a[s]+1)%2;
if (x>=0&&x<16)
a[x]=(a[x]+1)%2;
if ((y>=0&&y<16)&&i%4!=3) //当前棋子在右边界时,与其相邻的右方的棋子不能翻转
a[y]=(a[y]+1)%2;
if ((z>=0&&z<16)&&i%4!=0) //当前棋子在左边界时,与其相邻的左方的棋子不能翻转
a[z]=(a[z]+1)%2;
//printf("%d %d %d %d\n",a[s],a[x],a[z],a[y]);
u=check();
//printf("%d\n",u);
if (step[u]==-1||(step[u]>step[temp]+1)) //判断当前节点是否已扩展过,并且是否是最优。
{
queue[qe++]=u;
step[u]=step[temp]+1;
}
//将翻转过的棋子复位,供下一次扩展
a[i]=(a[i]+1)%2;
if (s>=0&&s<16)
a[s]=(a[s]+1)%2;
if (x>=0&&x<16)
a[x]=(a[x]+1)%2;
if ((y>=0&&y<16)&&i%4!=3)
a[y]=(a[y]+1)%2;
if (z>=0&&z<16&&i%4!=0)
a[z]=(a[z]+1)%2;
//int t=check();
//printf("\n");
//printf("%d %d\n",u,step[u]);
}
}
}
int main()
{
//while (1)
//{
int i,j,s=1;
char c;
n=0; //记录初始状态的十进制数
//memset(flag,0,sizeof(flag));
memset(step,-1,sizeof(step));
for (i=0;i<4;i++)
{
for (j=0;j<4;j++)
{
if (i!=0||j!=0)
s*=2;
c=getchar();
if (c=='b')
{
n+=s*1;
}
else
{
n+=s*0;
}
//printf("%d\n",n);
}
getchar();
}
//printf("%d\n",n);
m=-1;
//system("pause");
bfs();
if (m!=-1)
printf("%d\n",m);
else
printf("Impossible\n");
//system("pause");
//}
return 0;
}