P2324 [SCOI2005]骑士精神(A*)

P2324 [SCOI2005]骑士精神

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;
} 

猜你喜欢

转载自www.cnblogs.com/kafuuchino/p/11285586.html