POJ 2046 Gap(BFS+hash判重)
http://poj.org/problem?id=2046
题意:
4*8的方格有28个数字,按照给定的规则移动,现在要你从初始状态移动到终结状态,问你最少几步.
分析:
依然是BFS,且每个状态需要的元素很多。所以用dist[][][][][]...[]这种形式空间不足,所以需要用hash判重且保存所有出现过的状态。
首先我们每个状态要用一个32位的char数组s保存32个方格中的每个数字,然后我们用e[4]数组保存32个方格中(有4个0)4个0的位置分别是多少,并且用p[48]保存XY(11<=XY<=47)这个数字在s数组中的位置.用dist表示我们最少走几步能到该状态s[32]
这个题目还要用到hash来判断我们新生成的状态s是否已经出现了.
本题大体框架和一般的BFS问题没区别,关键在于注意各种细节,别出错.
AC代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000000+7;
struct node
{
char s[32],e[4],p[48];
int dist; //错误1,忘了写dist
}Q[maxn/2];
int head[maxn],next[maxn/2];//数组大小探测得出,head[i]==-1表示不存在
int hash(char *s)//获得哈希值
{
unsigned int v=0;
for(int i=0;i<=31;i++) v=v*7+s[i];
return v%maxn;
}
void insert(int i)//我们先find(s)之后知道i不存在,才直接insert
{
int h=hash(Q[i].s);
next[i]=head[h];
head[h]=i;
}
int cmp(char *s1,char *s2)//不同返回1,相同返回0
{
for(int i=0;i<=31;i++)
if(s1[i]!=s2[i]) return 1;
return 0;
}
void cpy(char *s1,char *s2,int n)
{
for(int i=0;i<n;i++) s1[i]=s2[i];
}
int find(char *s)
{
int h=hash(s);
int u=head[h];
while(u>=0)
{
if(cmp(Q[u].s,s)==0) return u;
u=next[u];
}
return -1;//未找到
}
void get(char &a)
{
char ch=getchar();
while(!(ch>='0'&&ch<='9')) ch=getchar();
for(a=0; (ch>='0'&&ch<='9') ;ch=getchar()) a=a*10+ch-'0';
if(a==11||a==21||a==31||a==41) a=0;
}
int BFS()
{
int front=1,tail=2,suc_v,suc_p,e_pos,k;//suc_v后继的值,suv_p后继的位置,e_pos表0的位置
while(tail>front)
{
for(int i=0;i<4;i++)//对于4个0的位置
{
cpy(Q[tail].s,Q[front].s,32);
if((e_pos=Q[front].e[i])>0 && Q[front].s[e_pos-1]%10<7 && Q[front].s[e_pos-1]!=0 )
{//上面3个判断对应于: 0的左边不是边界 0的坐左边值有后继 0的左边不是0
Q[tail].s[e_pos]=suc_v=Q[front].s[e_pos-1]+1;
Q[tail].s[ suc_p=Q[front].p[suc_v] ]=0;
if( (k=find(Q[tail].s))<0)//该状态原先没有
{
cpy(Q[tail].e, Q[front].e,4);
cpy(Q[tail].p, Q[front].p,48);
Q[tail].e[i]=suc_p;
Q[tail].p[suc_v]=e_pos;
Q[tail].dist=Q[front].dist+1;
insert(tail);
tail++;
}
else if(k==0) return Q[front].dist+1;//找到了终态
}
}
front++;
}
return -1;
}
int main()
{
for(int i=0;i<4;i++)
for(int j=0;j<7;j++)
Q[0].s[i*8+j]=(i+1)*10+j+1; //错误2 写成了(i/8+1)*10+j+1 了
int T;
scanf("%d",&T);
while(T--)
{
memset(head,-1,sizeof(head));
insert(0);
for(int i=0;i<4;i++)
for(int j=1;j<=7;j++)
get(Q[1].s[i*8+j]) , Q[1].p[Q[1].s[i*8+j]]=i*8+j;
for(int i=0;i<4;i++)//处理第一列
Q[1].s[i*8]=(i+1)*10+1 , Q[1].p[Q[1].s[i*8]] = i*8;
if(find(Q[1].s)==0) printf("0\n");
else
{
int k=0;
for(int i=0;i<4;i++)
for(int j=0;j<8;j++)if(Q[1].s[i*8+j]==0)
Q[1].e[k++]=i*8+j;
Q[1].dist=0;
insert(1);
printf("%d\n",BFS());
}
}
return 0;
}