POJ_1753
这个题目第一次看也确实没有什么思想,后来到网上看了一下“点灯游戏”的简介,也获得了不少的启发。
这个题目关键的算法思想在于,对最少操作次数而言,由于每个棋子点击偶数次相当于没点,同时点击的顺序并不影响最终结果,所以每个棋子要么点击一次,要么干脆不点,理解了这一点之后思路便是大体形成了。
于是把黑、白棋布局用1、0存储,再用一个4*4的数组和16层for循环来枚举操作的所有情况,之后一一计算每种操作是否能达到预期效果以及其所需的操作次数即可,所有的情况是2^16种,计算量还是可以接受的。
(中间过了很多天……)
时隔一个月之后,已经做完了八数码,便迫不及待的想找个题来巩固一下做八数码学到的东西,于是又用BFS+Hash判重重新做了这个题目。
一开始本来还想额外用一个数组op记录对各个棋子是否反动来减少无用的操作的,但是后来反倒时间会比较长,而且用C交还会出现RE,可能是相比直接BFS来回整块复制op既浪费了空间又浪费了时间吧,于是干脆把op抹掉了,果然用C交也AC了,而且效率还更高了。
P.S.这个题目我又重新用更简单、高效的方式写一遍,发在了另外一篇解题报告里面了。
http://www.cnblogs.com/staginner/archive/2011/10/29/2228784.html
#include<stdio.h>
#include<string.h>
#define MAX 100000
#define HASH 100003
int st[MAX][16],dis[MAX],goal1[16],goal2[16];
int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
int first[HASH],next[MAX];
char b[10];
void lookup()
{
memset(first,-1,sizeof(first));
}
int hash(int s)
{
int v=0,i;
for(i=0;i<16;i++)
v=2*v+st[s][i];
return v%HASH;
}
int insert(int s)
{
int u,h;
h=hash(s);
for(u=first[h];u!=-1;u=next[u])
if(memcmp(st[u],st[s],sizeof(st[s]))==0)
return0;
next[s]=first[h];
first[h]=s;
return1;
}
int bfs()
{
int d,x,y,z,newx,newy,newz,front,rear;
lookup();
dis[1]=0;
front=1;
rear=2;
while(front<rear)
{
if(memcmp(st[front],goal1,sizeof(goal1))==0)
return front;
if(memcmp(st[front],goal2,sizeof(goal2))==0)
return front;
for(z=0;z<16;z++)
{
memcpy(st[rear],st[front],sizeof(st[front]));
st[rear][z]=!st[rear][z];
x=z/4;
y=z%4;
for(d=0;d<4;d++)
{
newx=x+dx[d];
newy=y+dy[d];
if(newx>=0&&newx<4&&newy>=0&&newy<4)
{
newz=newx*4+newy;
st[rear][newz]=!st[rear][newz];
}
}
if(insert(rear))
{
dis[rear]=dis[front]+1;
rear++;
}
}
front++;
}
return0;
}
int main()
{
int i,j,k,ans;
while(scanf("%s",b)==1)
{
for(j=0;j<4;j++)
{
if(b[j]=='b')
st[1][j]=1;
else
st[1][j]=0;
}
for(i=1;i<4;i++)
{
scanf("%s",b);
for(j=0;j<4;j++)
{
if(b[j]=='b')
st[1][i*4+j]=1;
else
st[1][i*4+j]=0;
}
}
for(i=0;i<16;i++)
{
goal1[i]=1;
goal2[i]=0;
}
ans=bfs();
if(ans==0)
printf("Impossible\n");
else
printf("%d\n",dis[ans]);
}
return0;
}