题目大意:有一个4*4的方格,每个方格中放一粒棋子,这个棋子一面是白色,一面是黑色。游戏规则为每次任选16颗中的一颗,把选中的这颗以及它四周的棋子一并反过来,当所有的棋子都是同一个颜色朝上时,游戏就完成了。现在给定一个初始状态,要求输出能够完成游戏所需翻转的最小次数,如果初始状态已经达到要求输出0。如果不可能完成游戏,输出Impossible。
题解:状压+广搜???
我们可以把一个格子的摆设情况转化为二进制表示,若为b则是1,否则为0,。
那么我们可以用一个二进制数表示一个期盼的状态,例如:
bbww
bwww
wwww
wwww
1100100000000000,十进制值为51200
那么全白为0,全黑就是65535.接下来我们可以进行广搜,直到到达这两种状态之一就可以返回值;
我们每一次变化之后的状态十进制值就是原状态的十进制值与我们算出的状态数组ch[]异或的值~
至于ch[]数组:对于每一个格子,上下左右都要反转,则一共有16中方式,其分别对应了这个数组中的一个值;
以下AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define rep(i,a,b) for(int i=(a);i<(b);i++)
using namespace std;
bool vis[65540];
struct node{
int state,step;
};
int ch[16]={
51200,58368,29184,12544,
35968,20032,10016,4880,
2248,1252,626,305,
140,78,39,19
};//几种反转一个格子的情况的二进制所表示的十进制数
char str[10][10];
int bfs(int zt){
memset(vis,0,sizeof vis);
queue<node>q;
node no,ne;
no.state=zt;no.step=0;
q.push(no);
vis[zt]=1;
while(!q.empty()){
no=q.front();q.pop();
if(no.state==0 || no.state==65535)return no.step;
for(int i=0;i<16;i++){
ne.state=no.state^ch[i];
ne.step=no.step+1;
if(vis[ne.state])continue;
if(ne.state==0 || ne.state==65535)return ne.step;
vis[ne.state]=1;
q.push(ne);
}
}
return -1;
}
int main(){
int now=0;
rep(i,0,4){
cin>>str[i];
}
rep(i,0,4){
rep(j,0,4){
now<<=1;
if(str[i][j]=='b')now++;
}
}
int ans=bfs(now);
if(ans==-1)printf("Impossible\n");
else printf("%d\n",ans);
return 0;
}