网上都是A*算法,这里dfs通过估价函数剪枝,过了。
实际上就是判断最少移动多少次可以到目标状态,如果当前移动次数的加上预期移动次数大于ans,就直接返回
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int mod=1635947;
const int end[5][5]=
{
{1,1,1,1,1},
{0,1,1,1,1},
{0,0,3,1,1},
{0,0,0,0,1},
{0,0,0,0,0}
};
const int qx[]={1,1,2,2,-1,-1,-2,-2};
const int qy[]={-2,2,-1,1,-2,2,-1,1};
int ans;
bool hs[1635947];
struct aa
{
int a[5][5],x,y;
int get_hs()
{
int hs=0;
for (int i=0;i<5;i++)
for (int j=0;j<5;j++)
{
hs=(hs<<1)+a[i][j];
if (hs>=mod) hs-=mod;
}
return hs;
}
int pan()
{
int tt=0;
for (int i=0;i<5;i++)
for (int j=0;j<5;j++) if (a[i][j]!=end[i][j]) tt++;
return tt;
}
}s;
bool pan(int x,int y)
{
if (x<0||x>4||y<0||y>4) return false;
return true;
}
void dfs(aa &s,int step)
{
if (step>ans) return ;
int tt=s.pan();
if (tt==0) {ans=step-1;return;}
if (step+tt-2>ans) return ;
int x=s.x,y=s.y;
for (int i=0;i<8;i++)
{
int xx=x+qx[i],yy=y+qy[i];
if (!pan(xx,yy)) continue;
swap(s.a[x][y],s.a[xx][yy]);
s.x=xx,s.y=yy;
int h=s.get_hs();
if (!hs[h])
{
hs[h]=true;
dfs(s,step+1);
hs[h]=false;
}
swap(s.a[x][y],s.a[xx][yy]);
}
s.x=x,s.y=y;
}
void work()
{
memset(hs,false,sizeof(hs));
char ch[6];
for (int i=0;i<5;i++)
{
scanf("%s",ch);
for (int j=0;j<5;j++) if (ch[j]!='*')
s.a[i][j]=ch[j]-'0';
else s.a[i][j]=3,s.x=i,s.y=j;
}
ans=16;
int h=s.get_hs();
hs[h]=true;
dfs(s,1);
if (ans==16) printf("-1\n");
else printf("%d\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while (T--) work();
return 0;
}
总结
1:对于hash判重,在一些求最少步数的dfs中,递归调用结束后需要注意把hash【h】置为false
2:搜索的估价函数剪枝实用性非常强,注意灵活应用