题目链接:http://poj.org/problem?id=1753
题目大意:在一个4*4的矩阵中,由16快一面是黑色,一面是白色的圆盘组成,现在照如下操作:
1.在16快圆盘中任选一块,
2.翻转该块圆盘和它上下左右的圆盘
问如何能在翻转次数最少的情况下将16快圆盘翻成全是白色或者黑色,输出最少的翻转次数,如果怎么都达不到目标,则输出“Impossible”。
思路:第一道位运算(只要是异或来翻转圆盘)题目,总共16块圆盘,所以16个二进制位就可以了,‘1’代表白色,‘0’代表黑色,所以最终状态是:“1111 1111 1111 1111”或“0000 0000 0000 0000”即代表成功了。具体看代码,有相对详细的解释。
#include<stdio.h>
#include<math.h>
#include<string.h>
typedef struct {
int zt;
int flips;
} state;
state q[65536];//队列
int head=0,tail=-1;
int flip[16]={19,39,78,140,305,626,1252,2248,4880,10016,20032,35968,12544,29184,58368,51200};
/* 对应翻转:
10011 100111 1001110 10001100
100110001 1001110010 10011100100 100011001000
1001100010000 10011100100000 100111001000000 1000110010000000
11000100000000 111001000000000 1110010000000000 1100100000000000
*/
int fliped[65536];//标记状态是否出现过,由于总共只有65535种状态
char map[4][4];
void enq(state s) //进队
{
++tail;
q[tail].zt=s.zt;
q[tail].flips=s.flips;
fliped[q[tail].zt] = 1;
}
void deq()//出队
{
++head;
}
state gethead()//取队首元素
{
return q[head];
}
int isempty()//判空
{
if(head>tail)
return 1;
else
return 0;
}
int bfs()
{
int i;
state temp,temp1;
memset(fliped,0,sizeof(fliped));
enq(q[0]);
while(!isempty())
{
temp=gethead();
deq();
if(temp.zt==0 || temp.zt == 65535)//达到了目标状态
return temp.flips;
else
{
for(i=0;i<16;i++)
{
temp1.zt =temp.zt ^ flip[i];//位异或翻转圆盘
if(!fliped[temp1.zt])//该状态没有出现过,入队,对应的翻转次数加1
{
temp1.flips=temp.flips+1;
enq(temp1);
}
}
}
}
return -1;
}
int main()
{
int i,j,minflip;
int Inizt=0;
for(i=0;i<4;i++)
scanf("%s",map[i]);
for(i=0;i<4;i++)//求出初始状态
for(j=0;j<4;j++)
{
if(map[i][j] - 'w' == 0)
Inizt+=(int)pow(2,(double)(i*4+j));
}
q[0].zt=Inizt;
q[0].flips=0;
minflip=bfs();
if(minflip == -1)
printf("Impossible\n");
else
printf("%d\n",minflip);
return 0;
}