• 学习笔记——A* /IDA*


    A* /IDA* 算法(Astar/IDAstar)初步学习笔记

    0x01 引入

    看一道sb题

    看完题的我:

    我:省选题竟然还有爆搜题目!看我用BFS过了他

    于是我满心欢喜地打完了朴素的不能再朴素的BFS,交了上去

    恭喜!你TLE+MEL了此题!

    W D N M D

    显然,若使用BFS,到第十层的状态数将会是(8^{10})个,空间很快就会爆炸

    那么……

    我:限制层数为15!我要DFS!

    这次没有爆空间!AMDDFS,YES!

    恭喜!你TLE了此题!

    我:……

    DFS仍然不能解决状态数过多的情况(哪怕加上一些剪枝)

    那么……

    0x02 解决

    考虑一个事实:

    设还有x个骑士没有归位,则最好情况下要移动x次,每一次都让一个骑士归位

    跟着这个事实,我们可以剪掉大量的无用状态

    具体来讲,我们设到达当前状态用了(f(x))部,还有(g(x))个骑士未归位

    根据上述事实,若(f(x)+g(x))大于限制步数,则无法在规定步数内全部归位,这个状态就可以剪掉

    这似乎是个很玄学的剪枝,但事实证明它很有效

    为什么呢,因为它交上去过了

    0x03 总结

    上一节的剪枝包含了A*的主要思想

    细心的同学们应该发现了,我们刚刚使用了(f(x))(g(x))来描述“来到当前状态所用的步数”和“至少需要的个数”

    事实上,这不是个巧合

    下面我们搬出来自己YY的A*定义

    设起点到当前状态的距离(在例题中为步数)为(f(x)),估计当前状态到终点的距离为(g(x)),距离限制为(u(x))

    则若(f(x)+g(x)>u(x)),当前状态无效

    漂亮地下了个并不完整的定义,不过大部分情况可以这么理解

    0x04 扩展——IDA*

    我们可以看到,在上一题中,(u(x))是定值,为15

    那么有没有不是定值的呢?

    再看一道题

    我知道可以双向BFS过不用再说了

    看到这道题:

    DFS:搜索的能力是有极限的,越是沉迷剪枝,就越会发现搜索是有极限的...除非成为超越搜索的存在。

    BFS:你到底想说什么啊DFS?

    DFS:BFS!我不做搜索了!

    首先,这道题看上去和上一题并没有什么本质上的区别,所以无脑上A*

    等等,(u(x))死哪去了?

    当然没有……

    经过手玩一些东西,我们发现最优答案的层数大概率不会很大

    那我们……设定一个(u(x))

    感觉很难定下来的样子……

    既然不会枚举很多,那我们干脆……枚举(u(x))

    似乎很不科学,但我也想不到什么更好的办法了

    那就上吧

    于是非常轻易地打出了按照这种思路的搜索

    然后就过了……

    后就过了……

    就过了……

    过了……

    了……

    考虑一个事情:
    搜索树每增加一层,节点数增加固定倍数

    大概是这样:

    第一层:(x^0)

    第二层:(x^1)

    第三层:(x^2)

    第四层:(x^3)

    第五层:(x^4)

    (cdots)

    以此类推,第n层的节点数为(x^{n-1})

    根据我们在小学二年级就学过的等比数列求和公式:

    [S_n = a_1 frac{1-q^n}{1-q} ]

    (q为公比)

    可以得到

    [S_{n-1}<a_n ]

    所以,大部分时间都耗在了最后一层上

    因此,时间复杂度应该是科学的……

    事实上,这种枚举上限的方法就叫做IDA*

    全名迭代加深A*搜索……

    这种方法使用的地方已经说过了,在这里强调一下:

    整棵搜索树的状态总数非常大,且高度无法估计,但答案的深度很小(相对于整棵搜索树)

    0x05 代码

    骑士精神(IDA*)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int ans[5][5]={{1,1,1,1,1},
                         {0,1,1,1,1},
                         {0,0,-1,1,1},
                         {0,0,0,0,1},
                         {0,0,0,0,0}};
    
    const int d[8][2]={{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};//same:x+y=3/x+y=11
    
    inline void read(int &x) {
        x=0;
        int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {
            if (ch=='-') {
                f=-1;
            }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') {
            x=x*10+ch-'0';
            ch=getchar();
        }
        x*=f;
    }
    
    int a[5][5];
    int n;
    
    inline int check() {
        for(int i=0;i<5;i++) {
            for(int j=0;j<5;j++) {
                if (a[i][j]!=ans[i][j]) {
                    return 0;
                }
            }
        }
        return 1;
    }
    
    int found;
    
    inline int count() {
        int cnt=0;
        for(int i=0;i<5;i++) {
            for(int j=0;j<5;j++) {
                if (a[i][j]!=ans[i][j]) {
                    cnt++;
                }
            }
        }
        return cnt;
    }
    
    void Astar(int step,int x,int y,int max_step,int pre) {
        if (step==max_step) { 
            if (check()) {
                found=1;
            }
            return;
        }
        if (found) {
            return;
        }
        for(int i=0;i<8;i++) {
            int x1=x+d[i][0];
            int y1=y+d[i][1];
            if (pre!=-1) {
                if ((pre<4&&i+pre==3)||(pre>=4&&i+pre==11)) {
                    continue;
                }
            }
            if (x1<0||x1>4||y1<0||y1>4) {
                continue;
            }
            swap(a[x][y],a[x1][y1]);
            if (count()+step<=max_step&&!found) {
                Astar(step+1,x1,y1,max_step,i);
            }
            swap(a[x][y],a[x1][y1]);
        }
    }
    
    int main() {
        read(n);
        while(n--) {
            found=0;
            int sx,sy;
            for(int i=0;i<5;i++) {
                for(int j=0;j<5;j++) {
                    char ch;
                    cin>>ch;
                    a[i][j]=ch-'0';
                    if (ch=='*') {
                        a[i][j]=-1;
                        sx=i;
                        sy=j;
                    }
                }
            }
            if (check()) {
                printf("%d
    ",0);
                continue;
            }
            int max_step=1;
            for(;max_step<=15;max_step++) {
                Astar(0,sx,sy,max_step,-1);
                if (found) {
                    break;
                }
            }
            if (found) {
                printf("%d
    ",max_step);
            } else {
                printf("-1
    ");
            }
        }
        return 0;
    }
    
    

    八数码难题(IDA*)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int d[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
    const int ans[3][3]={{1,2,3},
                         {8,0,4},
                         {7,6,5}};
    
    inline void read(int &x) {
        x=0;
        int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {
            if (ch=='-') {
                f=-1;
            }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') {
            x=x*10+ch-'0';
            ch=getchar();
        }
        x*=f;
    }
    
    int a[3][3];
    
    inline int check() {
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++) {
                if (a[i][j]!=ans[i][j]) {
                    return 0;
                }
            }
        }
        return 1;
    }
    
    int found;
    
    inline int count() {
        int cnt=0;
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++) {
                if (a[i][j]!=ans[i][j]) {
                    cnt++;
                }
            }
        }
        return cnt;
    }
    
    void Astar(int step,int x,int y,int max_step,int last_step) {
        if (step==max_step) {
            if (check()) {
                found=1;
            }
            return;
        }
        if (found) {
            return;
        }
        for(int i=0;i<4;i++) {
            int x1=x+d[i][0];
            int y1=y+d[i][1];
            if (i+last_step==3) {
                continue;
            }
            if (x1<0||x1>2||y1<0||y1>2) {
                continue;
            }
            swap(a[x][y],a[x1][y1]);
            if (!(count()+step>max_step)&&!found) {
                Astar(step+1,x1,y1,max_step,i);
            }
            swap(a[x][y],a[x1][y1]);
        }
    }
    
    int main() {
        int sx,sy;
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++) {
                char ch=getchar();
                a[i][j]=ch-'0';
                if (!a[i][j]) {
                    sx=i;
                    sy=j;
                }
            }
        }
        if (check()) {
            printf("%d
    ",0);
            return 0;
        }
        int max_step=0;
        for(;;) {
            max_step++;
            Astar(0,sx,sy,max_step,-1);
            if (found) {
                break;
            }
        }
        printf("%d
    ",max_step);
        return 0;
    }
    
  • 相关阅读:
    新的学期,新的学习
    织梦最新
    selectd选项跳转链接
    字体统一大小
    js点击后进入到另一个页面的TAB选项中
    织梦套后台
    js获取距离各边的距离
    将时间的秒数转化为分钟数
    jquery手写tab选项卡功能
    js中的定时器
  • 原文地址:https://www.cnblogs.com/tt66ea-blog/p/12110990.html
Copyright © 2020-2023  润新知