题意:给出初始图找出可达目标图,步数最多的一个,并输出路径。
这题很像八数码格。情况有9!种。
用康托展开来进行哈希(哈希值等于当前数是未出现数字中第几个,然后乘上(n-i)!)
比如 1 3 2 0 4
那么hash=1*4!+2*3!+1*2!+0*1!+0*0!;
然后进行bfs,用一个vis数组标记已经出现过的状态(hash值)
那么队列种最后留下来的肯定就是步数最多的了。
AC代码
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
int hashh[10];
int vis[400000];
struct tree
{
int hash;
int x,y;
//0的位置,避免多次查找
int map[3][3];
};
int prehash[400000],preforword[400000];
//用来记录路径
int dir[4][2]={-1,0,0,-1,1,0,0,1};
char m[10]="ULDR";
int gethash(int a[])
//hash函数
{
int ha=0,t=0;
int f[10];
memset(f,0,sizeof(f));
for(int i=0;i<9;i++)
{
t=0;
for(int j=0;j<9;j++)
if(j<a[i]&&!f[j])
t++;
f[a[i]]=1;
ha+=t*hashh[8-i];
}
return ha;
}
int main()
{
int T,cas=0;
scanf("%d",&T);
hashh[0]=1;
for(int i=1;i<=8;i++)
hashh[i]=hashh[i-1]*i;
while(T--)
{
memset(prehash,0,sizeof(prehash));
memset(preforword,0,sizeof(preforword));
memset(vis,0,sizeof(vis));
int a[10];
tree p;
for(int i=0;i<9;i++)
{
scanf("%d",&a[i]);
if(a[i]==0)
p.x=i/3,p.y=i%3;
}
p.hash=gethash(a);
int jj=0,ii=0;
for(int i=0;i<9;i++)
{
if(i%3==0&&i!=0)
ii++,jj=0;
p.map[ii][jj]=a[i];
jj++;
}
queue<tree>q;
q.push(p);
vis[p.hash]=1;
preforword[p.hash]=-1;
int aim=p.hash;
while(!q.empty())
{
p=q.front();
q.pop();
for(int i=0;i<4;i++)
{
tree pp=p;
if(p.x+dir[i][0]<0||p.x+dir[i][0]>=3||p.y+dir[i][1]>=3||p.y+dir[i][1]<0)
continue;
pp.map[p.x][p.y]=pp.map[p.x+dir[i][0]][p.y+dir[i][1]];
pp.map[p.x+dir[i][0]][p.y+dir[i][1]]=0;
pp.x=p.x+dir[i][0];
pp.y=p.y+dir[i][1];
int aa[10],ttt=0;
for(int ii=0;ii<3;ii++)
{
for(int jj=0;jj<3;jj++)
aa[ttt++]=pp.map[ii][jj];
}
pp.hash=gethash(aa);
if(!vis[pp.hash])
{
preforword[pp.hash]=i;
prehash[pp.hash]=p.hash;
vis[pp.hash]=1;
q.push(pp);
}
}
}
printf("Puzzle #%d\n",++cas);
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
printf("%d",p.map[i][j]);
if(j!=2)
printf(" ");
}
printf("\n");
}
char anss[400000];
int tp=0;
while(preforword[p.hash]!=-1)
{
anss[tp++]=m[preforword[p.hash]];
p.hash=prehash[p.hash];
}
for(int i=tp-1;i>=0;i--)
printf("%c",anss[i]);
printf("\n\n");
}
}