题目链接:http://poj.org/problem?id=1753
题目描述:
4*4的棋盘上翻棋,翻动一个子其上下左右四子也会被翻过去,问对于给出的一个4*4布局,是否能全翻成白棋或黑棋,如果能输出步数
解题思路:
之前的例会上学长用这个给我们当了状态压缩的栗子,当然,当时一点不明白学长在表达什么,碰巧遇到这题,自己就做了一把。
大概知道这样题换成二进制数储存状态,具体怎么对某个数在某一个二进制位上取反,我是今天才学的。
用这个” ^ “异或运算符,它的意思是把两个数换成二进制,两个数的相同位上如果相同结果是0,不同是1。(感谢于哲松大神和群里某学长指点)
例:
7是0111
8是1000
7 ^ 8=1111=15
好的,我们用111....1111(十六个)表示这十六个子,它们构成的相应十进位制数代表了此时的状态(cur),如果他们全是0或者全是1(即十进制65535)就是全变成一个颜色了。我又设了一个op数组,数组中存储的值为2的0~15次幂,存储翻某个子造成影响。比如:cur ^ op[ i ][ j ]就表示把 i 行 j 列的棋子翻了过来,(因为 1^0 = 1,0^0 = 0,0^1 = 1,1^1 = 0)之后都好办了。。写完居然还WA了好几遍,发现main函数结尾那不能写成if(bfs( start )==-1)什么的,但是改成ans接收一下就AC了。。不知道为啥,好吧以后就都拿ans接住得了。。= =
AC代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
int op[5][5];
bool vis[70000];
queue<int> xx;
int dx[4]={1,-1,0,0},
dy[4]={0,0,1,-1};
bool ok(int x,int y)
{
if(x>=0&&x<4&&y>=0&&y<4)
return 1;
return 0;
}
int bfs(int st)
{
while(!xx.empty())xx.pop();
int step=0,cur,i,j,k;
xx.push(st);
while(!xx.empty())
{
int t=xx.size();
while(t--)
{
cur=xx.front();
xx.pop();
vis[cur]=1;
if(cur==0||cur==65535)
return step;
else
{
int next;
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
next=cur^op[i][j];
for(k=0;k<4;k++)
{
int tx,ty;
tx=i+dx[k];
ty=j+dy[k];
if(ok(tx,ty))
next=next^op[tx][ty];
}
if(!vis[next])
{
xx.push(next);
vis[next]=1;
}
}
}
}
}
step++;
}
return -1;
}
int input()
{
memset(op,0,sizeof(op));
memset(vis,0,sizeof(vis));
int ans=0,cc=0;
char grid[5][5];
int i,j;
for(i=0;i<4;i++)
scanf("%s",grid[i]);
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
op[i][j]=pow(2,cc);
if(grid[i][j]=='w')
ans+=pow(2,cc);
cc++;
}
return ans;
}
int main()
{
int start,i,j;
start=input();
int ans=bfs(start);
if(ans==-1)
cout<<"Impossible"<<endl;
else
cout<<ans<<endl;
return 0;
}
AC截图: