位运算可真是个好东西~
题目大意:题目题目
有一个4*4的黑白棋盘,棋盘上有黑子有白子,每次选取一个子后可以把它和它周围的四个子的颜色翻转,使它们从黑变成白或者从白变成黑。问你至少要多少操作可以使棋盘上全为黑子或者全为白子。
题目思路:
题目中固定了是4*4的棋盘,如果用二进制数字表示棋盘的状态,比如黑子为“1”,白子为“0”,那么一共有1<<16个状态,并不是很多,一共是65536。于是我们可以考虑开一个vis[maxn}的数组来存放每一个状态是否被访问过,其中maxn=65535(也可以大一些);然后就是正常的bfs;
还有一个比较关键的地方就是棋子翻转的时候如何把状态改变过来,看了别人的代码才发现,这时候……异或可真是个好东西。为了之后使用更加方便,我们可以提前预处理把每一位反转后要异或的数值算出来存在数组里。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 100000
using namespace std;
//int bin[16];
int vis[maxn];
struct TRE
{
int stus;
int num;
}start,mid,nxt;
queue<TRE>q;
int t[20] = {51200, 58368, 29184, 12544,35968, 20032, 10016, 4880,2248, 1252, 626, 305,140, 78, 39, 19};//预处理的数组
int main(void)
{
while(!q.empty())
q.pop();
int sum=0;
char ch[5][5];
int flag=1;
//memset(bin,0,sizeof(bin));
memset(vis,0,sizeof(vis));
for(int i=0;i<4;i++)
{
scanf("%s",ch[i]);
}
int cnt=15;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(ch[i][j]=='b')
sum+=(1<<cnt);//算出来初始的棋盘的十进制表示的值
cnt--;
}
}
vis[sum]=1;
start.stus=sum;
start.num=0;
q.push(start);
while(!q.empty())
{
mid=q.front();
q.pop();
if(mid.stus==0||mid.stus==65535)
{
flag=0;
printf("%d\n",mid.num);
break;
}
for(int i=0;i<16;i++)
{
nxt.stus=mid.stus^t[i];//状态的改变
//nxt.num=mid.num+1;
if(!vis[nxt.stus])
{
vis[nxt.stus]=1;
nxt.num=mid.num+1;
q.push(nxt);
}
}
}
if(flag)
printf("Impossible\n");
return 0;
}
呼呼