zoj 2050 - Flip Game
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2050
同样是一道bfs题,只不过难在如何对状态进行存储。我想了好长时间也没思路,看了别人的代码,理解后才写出来的。没法用状态数组标记(可能用set可以实现吧,没试过~~),所以用二进制来存储,比如都是白色,1111111111111111,最大时为2^16-1 = 65535,用很小的数组就可以存下。因为用到了二进制,就少不了位运算,因此要理解位运算后才能很好的理解这道题的算法。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef struct
{
int data;
int step;
}State;
queue<State> q;
int vis[65536];
int dx[] = {-1, 0, 1, 0};
int dy[] = { 0, -1, 0, 1};
State begin, end;
char s[4][4]; //black 1 white 0
int isright(int x, int y)
{
if(x < 0 || x > 3 || y < 0 || y > 3)
return 0;
return 1;
}
int main()
{
int ncases, i, j, bstate, ok, k, x, y, nx, ny;
scanf("%d", &ncases);
while(ncases--)
{
bstate = 0;
memset(vis, 0, sizeof(vis));
for(i = 0; i < 4; i++)
{
scanf("%s", s[i]);
for(j = 0; j < 4; j++)
{
if(s[i][j] == 'b')
{
bstate |= 1 << (i*4+j);
}
}
}
/*printf("%d\n", bstate);
for(i = 0; i < 4; i++)
{
for(j = 0; j < 4; j++)
printf("%c", s[i][j]);
putchar(10);
}*/
ok = 0;
begin.data = bstate;
begin.step = 0;
vis[begin.data] = 1;
q.push(begin);
while(!q.empty())
{
State u = q.front();
q.pop();
if(u.data == 0 || u.data == 65535)
{
ok = 1;
printf("%d\n", u.step);
break;
}
for(k = 0; k < 16; k++)
{
State v;
v.step = u.step + 1;
v.data = u.data ^ (1 << k);
x = k / 4;
y = k % 4;
for(i = 0; i < 4; i++)
{
nx = x + dx[i];
ny = y + dy[i];
if(isright(nx, ny))
{
v.data = v.data ^ (1 << (nx*4+ny));
}
}
if(!vis[v.data])
{
q.push(v);
vis[v.data] = 1;
}
}
}
if(!ok)
{
printf("Impossible\n");
}
if(ncases)
putchar(10);
while(!q.empty())
q.pop();
}
return 0;
}
附一些位运算的知识:
按位与的用途:
(1)清零
若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件:
原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。
(2)取一个数中某些指定位
若有一个整数a(2byte),想要取其中的低字节,只需要将a与8个1按位与即可。
按位或的用途:
应用:按位或运算常用来对一个数据的某些位定值为1。
异或用途:
使特定位翻转
设有数01111010,想使其低4位翻转,即1变0,0变1.可以将其与00001111进行“异或”运算。