SCOI2005-骑士精神

chunlvxiong的博客


题目描述:

  给出一个5*5的棋盘,每个骑士可以走日字走到空格位置,问最少几步形成如下局面。

  如果最少步数超过15步,输出-1。

思考&分析:

  搜索无非也就是深搜或广搜,如果广搜的话由于总状态数为25*C(24,12)=67603900,每个状态存5*5的棋盘的话,空间是炸飞的,所以你得考虑深搜。

  这里用IDA*可以达到较好的效果。

  所谓IDA*,也就是迭代加深与A*的结合:

  1、迭代加深:

    由于可能出现这样的情况:答案很浅,搜索树很庞大,但是你却往错误的方向一直搜索,导致你搜到答案的时间很迟。

    对于这种情况,你先二分一个深度k,保证搜索深度不超过这个k,看看能不能搜到,如果能那么将深度调小,否则将深度调大。

    (本题由于k≤15,所以枚举和二分耗时差不多)。

  2、A*

    A*原本是广搜的东西,但在深搜中也可以应用,就是先往距离目标近的方向去搜。

    本题的估价函数g(x):对于当前的局面,与最终局面不一样的位置个数。

    为什么选择这个估价函数呢?主要是因为计算这个估价函数的代价很低:直接利用上一状态的估价函数,计算代价O(1)。

    (本题比较奇怪,不加A*跑出来100ms,加A*跑出来200ms)。

  所以大致做法是:

  1、二分深度k并验证

  2、如果当前状态的g(x)=0,那么说明找到解并返回

  3、这个剪枝上面没提到:在本题中由于一次移动最多调整一个位置,所以对于某个状态,至少需要g(x)-1步(因为空格的存在)进行调整,也就是说,如果step+g(x)-1>k,那么说明一定搜不到了。

  4、对于当前状态转移到下一状态(最多8个),并计算下一状态的g(x)。(如果采取A*做法就是把所有下一状态的g(x)排个序,然后先搜小的)

  5、还原状态

贴代码:

不加A*:

#include <bits/stdc++.h>
using namespace std;
const int dx[8]={-2,-2,-1,-1,2,2,1,1};
const int dy[8]={-1,1,-2,2,-1,1,-2,2};
const char goal[5][5]={'1','1','1','1','1',
                       '0','1','1','1','1',
                       '0','0','*','1','1',
                       '0','0','0','0','1',
                       '0','0','0','0','0'};
char Map[5][5];
int Max_step;
bool IDA(int x,int y,int step,int g){
    int new_x,new_y,new_g,cnt=0;
    if (!g) return true;
    if (step+g-1>Max_step) return false;
    for (int i=0;i<8;i++){
        new_x=x+dx[i],new_y=y+dy[i];
        if (new_x<0 || new_x>4 || new_y<0 || new_y>4) continue;
        new_g=g;
        new_g-=(Map[x][y]!=goal[x][y]),new_g-=(Map[new_x][new_y]!=goal[new_x][new_y]);
        swap(Map[x][y],Map[new_x][new_y]);
        new_g+=(Map[x][y]!=goal[x][y]),new_g+=(Map[new_x][new_y]!=goal[new_x][new_y]);
        if (IDA(new_x,new_y,step+1,new_g)){
            swap(Map[x][y],Map[new_x][new_y]);
            return true;
        }
        swap(Map[x][y],Map[new_x][new_y]);
    }
    return false;
}
int main(){
    int T; scanf("%d",&T);
    while (T--){
        int G=0,kx,ky;
        for (int i=0;i<5;i++)
        for (int j=0;j<5;j++){
            Map[i][j]=getchar();
            while (Map[i][j]!='0' && Map[i][j]!='1' && Map[i][j]!='*') Map[i][j]=getchar();
            G+=(Map[i][j]!=goal[i][j]);
            if (Map[i][j]=='*') kx=i,ky=j;
        }
        int L=1,R=15,ans=-1;
        while (L<=R){
            Max_step=(L+R)>>1;
            if (IDA(kx,ky,0,G)) R=Max_step-1,ans=Max_step;
            else L=Max_step+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

加A*:

#include <bits/stdc++.h>
using namespace std;
const int dx[8]={-2,-2,-1,-1,2,2,1,1};
const int dy[8]={-1,1,-2,2,-1,1,-2,2};
const char goal[5][5]={'1','1','1','1','1',
                       '0','1','1','1','1',
                       '0','0','*','1','1',
                       '0','0','0','0','1',
                       '0','0','0','0','0'};
char Map[5][5];
int Max_step;
struct node{
    int x,y;
}state[20][10];
bool cmp(node a,node b){
    return a.y<b.y;
}
bool IDA(int x,int y,int step,int g){
    int new_x,new_y,new_g,cnt=0;
    if (!g) return true;
    if (step+g-1>Max_step) return false;
    for (int i=0;i<8;i++){
        new_x=x+dx[i],new_y=y+dy[i];
        if (new_x<0 || new_x>4 || new_y<0 || new_y>4) continue;
        new_g=g;
        new_g-=(Map[x][y]!=goal[x][y]),new_g-=(Map[new_x][new_y]!=goal[new_x][new_y]);
        swap(Map[x][y],Map[new_x][new_y]);
        new_g+=(Map[x][y]!=goal[x][y]),new_g+=(Map[new_x][new_y]!=goal[new_x][new_y]);
        state[step][cnt].x=i,state[step][cnt++].y=new_g;
        swap(Map[x][y],Map[new_x][new_y]);
    }
    sort(state[step],state[step]+cnt,cmp);
    for (int i=0;i<cnt;i++){
        new_x=x+dx[state[step][i].x],new_y=y+dy[state[step][i].x];
        swap(Map[x][y],Map[new_x][new_y]);
        if (IDA(new_x,new_y,step+1,state[step][i].y)){
            swap(Map[x][y],Map[new_x][new_y]);
            return true;
        }
        swap(Map[x][y],Map[new_x][new_y]);
    }
    return false;
}
int main(){
    int T; scanf("%d",&T);
    while (T--){
        int G=0,kx,ky;
        for (int i=0;i<5;i++)
        for (int j=0;j<5;j++){
            Map[i][j]=getchar();
            while (Map[i][j]!='0' && Map[i][j]!='1' && Map[i][j]!='*') Map[i][j]=getchar();
            G+=(Map[i][j]!=goal[i][j]);
            if (Map[i][j]=='*') kx=i,ky=j;
        }
        int L=1,R=15,ans=-1;
        while (L<=R){
            Max_step=(L+R)>>1;
            if (IDA(kx,ky,0,G)) R=Max_step-1,ans=Max_step;
            else L=Max_step+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/chunlvxiong/p/7391445.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值