P2324 [SCOI2005]骑士精神(A*)
A*与爆搜的不同就是它有一个估价函数$h(x)$
这个估价函数一般设为从当前状态到终点状态的估计最短步数,这样可以有效剪枝
但估计值必须严格小于等于实际剩余步数,否则会剪枝过度而影响正确性
$g(x),f(x)$分别为剩余步数和已走步数,则:
$g(x)=f(x)+h(x)$
本题中的$h(x)$可以设为未归位的棋子数+1
因为每一步最多使一个棋子归位(除最后一步一次归位2个棋子)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int d1[8]={1,1,2,2,-1,-1,-2,-2}; int d2[8]={2,-2,1,-1,2,-2,1,-1}; char a[9][9]; int re,col[9][9]; inline bool is(int x,int y){return (x==3&&y==3)?(a[x][y]=='*'):(a[x][y]==col[x][y]+'0');} void dfs(int d,int s,int x,int y){//d当前步数,s已归位棋子个数 if(d+24-s>=re||d>15) return ;//f(x)=d,g(x)=24-s if(s==25) {re=min(re,d); return ;} for(int i=0;i<8;++i){ int rx=x+d1[i],ry=y+d2[i],p=s; if(rx<1||5<rx||ry<1||5<ry) continue; p-=is(x,y)+is(rx,ry); swap(a[x][y],a[rx][ry]); p+=is(x,y)+is(rx,ry); dfs(d+1,p,rx,ry); swap(a[x][y],a[rx][ry]); } } int main(){ for(int i=1;i<=5;++i) for(int j=i;j<=5;++j) col[i][j]=1; col[4][4]=col[5][5]=0; int T,sum,fx,fy;scanf("%d",&T); while(T--){ sum=0; re=16; for(int i=1;i<=5;++i){ scanf("%s",a[i]+1); for(int j=1;j<=5;++j){ sum+=is(i,j); if(a[i][j]=='*') fx=i,fy=j; } }dfs(0,sum,fx,fy); if(re>15) re=-1; printf("%d\n",re); }return 0; }