IDA*——BZOJ1085/Luogu2324 [SCOI2005] 骑士精神

http://www.lydsy.com/JudgeOnline/problem.php?id=1085
https://www.luogu.org/problem/show?pid=2324
从ZJOI2017讲课学来的新东西。。。
这题也就是启发式搜索(或者称A*)
。。哦不对,是IDA*(迭代加深启发式搜索)

我们找到空位置以后直接搜索状态直到目标状态
但是显然的,暴搜是过不掉这题的
一个最优性剪枝:计算不在目标位置的骑士个数m,如果m不足以在15步范围内把所有骑士归位,剪枝(写出这个DFS就升级成为A*了)
但是这样好像还是会T啊(不过我没试过)
题中有说,步数大于15步输出-1,所以最深层数是15
有限制层数而且那么小为什么不用迭代加深呢
于是我们就可以把上面的A*优化成IDA*
也就是限制层数
这个时候把剪枝内容改一下:计算不在目标位置的骑士个数m,如果m不足以在k步范围内把所有骑士归位,剪枝(k是限制层数)
然后就可以愉快地AC了
后注:哦对,这题数据luogu中没有答案为0的情况,其实0也可以是答案当输入状态为最终状态时
感谢lc233提醒
我一开始的程序是可以被卡掉的,改好的程序在下面

#include<bits/stdc++.h>
using namespace std;
int a[10][10];
const int dx[9]={0,1,1,-1,-1,2,2,-2,-2};
const int dy[9]={0,2,-2,2,-2,1,-1,1,-1};
bool flag;
const int b[6][6]={{0,0,0,0,0,0},{0,1,1,1,1,1},{0,0,1,1,1,1},{0,0,0,2,1,1},{0,0,0,0,0,1},{0,0,0,0,0,0}};
inline bool check(int a[10][10]){//目标状态判定
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)if(a[i][j]!=b[i][j])return 0;
    return 1;   
}
inline bool A(int a[10][10],int p,int k){//最优性剪枝
    int m=0;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)if(a[i][j]!=b[i][j]){
            m++;
            if(m+p>k)return 0;
        }
    return 1;
}
inline void dfs(int p,int a[10][10],int k,int x,int y){
    if(flag)return;
    if(p==k){
        if(check(a))flag=1;
        return;
    }
    for(int i=1;i<=8;i++){
        int xx=x+dx[i],yy=y+dy[i];
        if(xx<1||xx>5||yy<1||yy>5)continue;
        swap(a[x][y],a[xx][yy]);
        if(A(a,p,k))dfs(p+1,a,k,xx,yy);
        swap(a[x][y],a[xx][yy]);
    }
}
int main()
{
    int t,x,y;scanf("%d",&t);
    while(t--){
        flag=0;
        for(int i=1;i<=5;i++){
            char c[10];scanf("%s",c+1);
            for(int j=1;j<=5;j++)
                if(c[j]=='*')a[i][j]=2,x=i,y=j;
                else a[i][j]=c[j]-'0';
        }
        for(int i=0;i<=15;i++){//i限制层数
            dfs(0,a,i,x,y);
            if(flag){
                printf("%d\n",i);
                break;
            }
        }
        if(flag)continue;
        printf("-1\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值