题目的大意
在一个4*4的方格里面放黑或者白之一的卡片,当选中一个的,选中的那个就会翻转(如果是白色变为黑色,如果是黑色变为白色),伴随着他四周(上下左右)的的卡片也会翻转。就像下面这种情况,
如图所示,假设我们点击第三行第一列的时候不仅仅第三行第一列会变换状态,相应的第二行第一列,第三行第二列,第四行第一列也会变换,当然如果左边有的话也会变换状态。上图对应输入为:
bwbw
wwww
bbwb
bwwb
当点击完第三行第一列的时候,会变为下面的状态:
bwbw
bwww
wwwb
wwwb
w代表white白色,b代表black黑色
输入:输入4*4只包含b
或者w
的矩阵
输出:通过几次能改变为全为白色或者全为黑色,如果不能输入impossible
。
解题思路
bfs+状态压缩
棋盘一共是有16方格,也就意味着有2^16(65535)种状态,
我们就假设黑色的棋子的状态为0,白色状态的棋子为1
每种方格只能被点击一次,比如我们在一个全为黑色点击了左上角的棋子,棋盘如下:
bbbb
bbbb
bbbb
bbbb
抽象为对应的状态棋盘
就变为
0000
0000
0000
0000
点击左上角的棋子之后,状态棋盘就变为:
1100
1000
0000
0000
我们默认上面的和左边的高位,所以对应的二进制转换为十进制就变为51200
当点击其他的时候也是一样的原因。
有了这16中状态我们就可以通过异或
来得到点击完相应的棋子之后的状态。
AC代码
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<set>
#include<iostream>
#include<stack>
using namespace std;
struct Node{
int state;
int step;
};
bool visit[65536];
int change[16] = //这个是点击每个棋子的状态
{
51200,58368,29184,12544,
35968,20032,10016,4880,
2248,1252,626,305,
140,78,39,19
};
int bfs(int state)
{
memset(visit,false,sizeof(visit));
Node cur;
Node next;
int i,j;
cur.step = 0;
cur.state= state;
visit[state] = true;
queue<Node>q;
q.push(cur);
while(!q.empty())
{
cur = q.front();
q.pop();
if(cur.state == 0 || cur.state == 0xffff)//当全为白或者全为黑的情况
return cur.step;
for(i=0;i<16;i++)//一共有16个棋子
{
next.state = cur.state^change[i];//得到点击棋子之后的状态
next.step = cur.step+1; //步骤+1
if(next.state == 0 || next.state == 0xffff)
return next.step;
if(visit[next.state]) //已经遍历过中的状态
continue;
visit[next.state] = true;//如果没有遍历过这种状态,改变为遍历过
q.push(next);//添加到队列中
}
}
return -1;
}
int main()
{
int i,j,state,ans;
char theMap[5][5];
while(scanf("%s",theMap[0])!=EOF)
{
for(i=1;i<4;i++)
scanf("%s",theMap[i]);
state = 0;
for(i=0;i<4;i++) //变输入变把原棋盘的二进制计算出来
for(j=0;j<4;j++)
{
state <<= 1;
if(theMap[i][j] == 'b')
state+=1;
}
//printf("[%d]\n",state);
ans = bfs(state);
if(ans == -1)
printf("Impossible\n");
else
printf("%d\n",ans);
}
return 0;
}