• 搜索算法—A*


    这东西,简直是被神化了一样。我从蓝书上查找了一些资料,以我的水平确实难以看懂他的表达。于是借助百度,发现大多数博客都在转载一篇似乎是翻译自国外的文章。写的很详细,但我在第一次阅读的时候并没有看懂,并且感觉专业性较高。比如其中又是OpenList又是CloseList的,初看确实有些头晕眼花。

    那么,在这里重新整理一下我理解A*的过程。

    概念理论

    这个算法似乎被广泛应用于游戏开发的寻路算法里。他是启发式搜索的算法。相比于传统的搜索,你可以理解为他是边搜边看和终点的偏差的,也就是他自己在尽可能修正自己的路线的感觉。

    那么怎么实现这个“让程序自我认识并修正路线”呢?

    我们引入一个经典的成语故事。

    话说,南辕北辙…xxxxxxxx

    是的,如果一个搜索算法的路径如同南辕北辙一样,其效率一定是极其低下的。那么在选择移动的时候,我们每次都要尽可能的去选一个靠近终点的移动。那么如何让程序知道某个点更靠近于终点呢?

    这里就要引入一个估价的概念。

    估价函数,它所实现的功能即为:以任意状态的输入,计算出该状态到目标状态产生代价的估计值。

    通过这个估价,我们让程序实现两个考虑点。“考虑中间某个点与初始点的距离”,“考虑中间某个点与终点的距离”。当两者之和最小时,最短路径也就自然产生。

     听完这些口胡(口头胡扯233),就可以去看一下大佬的文章了。

    以红警为例的A*算法讲解

    还能复习一波深广搜,多棒唷233

    看完这个可能虽然有了感觉,但还是觉得有些东西没被点明白。没关系,这个时候去进攻那个国外的文章,就轻松加愉快了。

    原文    翻译版

    这里面包含了A*算法的模拟过程,相当详细,大佬很强啊!

    小做整理

    由于转载进来的东西太多了…所以简单梳理一下。

    首先我们要维护几个东西:

    Open List:需要不断去处理的“受关注”的点列表

    Close LIst:已经“不需要受到关注”的点列表

    可能需要一个二叉堆,便于之后的“总是取出F最小值”的操作。

    完成一个功能:

    预估路径->这个功能可以求出的内容:

    F:最终预估值,有F=G+H

    G:从起点 A 移动到指定方格的移动代价

    H:从指定的方格移动到终点 B 的估算成本

    结束时的状态:

    目标点被加入OpenList。

    查找终点失败,并且 open list 是空的,此时没有路径。

    如何去维护两个List?/两个List的关系是什么?

    在初始的时候,我们将起点加入OpenList。

    在每次进行搜索的时候,都会取OpenList中F值最小的点做处理,然后将其移入CloseList。

    对最F最小点做了什么处理?

    对于被取出的F值最小的点,对其周边点进行如下处理(A、B、C中只择其一):

    A.当该周边点是不可达的(即该点为障碍物,不可被作为路径点)或存在于CloseList中时:

    忽略,不进行任何处理。

    B.当该周边点尚未处于OpenList中时:

    把它加入 Open List ,并且把当前方格设置为它的父亲(如果你需要具体路径的话),记录该方格的 F , G 和 H 值。

    C.该周边点已经位于OpenList中时:

    我们假设该节点的父节点被重载为F值最小点。用因为这个假设而产生的新的G值来与以起点为父节点产生的旧的G值做对比。如果新G值更小,那么更换该周边点的父节点,并为其重新计算F、G、H。

    如何去实现预估功能?

    这里仅仅介绍一种。

    曼哈顿距离(Manhattan Distance)

    这个距离算法,忽略障碍物,不斜着走。就是一道横线一道竖线通往终点。计算公式:d(i,j)=|xi-xj|+|yi-yj|

    一般用途

    k短路

    为了找代码,我在网上游荡了很久。发觉更多的都是被融在求第k短路径。虽然不知道为什么,但为了能方便呈现出A*的板子代码,这里决定先行分析现有资源

    什么是k短路问题?

    图片来源

    在研究学习过程中,发现这个东西是涉及到几个比较需要动脑子的知识点的。

    大概是这些:   小根堆    SPFA  

    因为这里涉及一个预处理路径的问题,所以我发现网上有人用SPFA,有人用dijkstra。那么各取所好吧。反正我觉得Dj用的频率好像多一点…

    题目中的Show Time

    魔法猪学院 SDOI2010

    题面传送

    据说LuoGu加强了数据,导致A*会有一个点被卡掉。但这个对我们不产生影响,因为我们只是想拿这道题来熟悉一下A*的代码实现而已。

    ----7.23补档

    关于A*,我已经研究了整整三天了。今天我终于悟到了这道题的写法(虽然代码依然不是我自己写的2333),但我前前后后写了有四个板子去试去撞。撞了一排又一排的WonderfulAnswer。然后今天的模拟赛我还又打了一手A*试了试,难得写对了跑掉了样例。(然而那道题用A*的话时间爆炸…一分都拿不到)。

    诉苦结束。说几个注意事项:

    跑Spfa的时候,是用反图跑的。你需要把边反转过来,而不是用正边跑。否则会得到一个满满的除了初始点是0其他都是初始化的距离数组。

    这道题不是经典无脑模板题,依然需要你手动去改一点东西。

    下面的代码有一些小不一样。在这里,我们用Spfa预处理的就是该点到终点的距离H。然而因为这代码不是我写的,所以这代码里的h是指代节点与出发点的距离。大家做一下区分。

    #include<bits/stdc++.h>
    using namespace std;
    int g,n,m,head[5005],tail[5005],ans,bj[200005];
    double ch[5005],e;
    struct node{
        int to,n;
        double w;
    }pr[200005],rpr[200005];
    struct data{
        int d;
        double h,f;
        bool operator < (const data &a) const {
            return f>a.f;
        }
    }zz,mm;
    priority_queue <data> q;
    queue <int> p;
    void add(int u,int v,double w){
        pr[++g].w=w;
        pr[g].to=v;
        pr[g].n=head[u];
        head[u]=g;
    
        rpr[g].w=w;
        rpr[g].to=u;
        rpr[g].n=tail[v];
        tail[v]=g;
    }
    void spfa()
    {
        memset(ch,0x7f,sizeof(ch));
        bj[n]=1;
        ch[n]=0;
        p.push(n);
        while(!p.empty())
        {
            int u=p.front();
            p.pop();
            bj[u]=0;
            for(int i=tail[u];i;i=rpr[i].n)
            {
                int v=rpr[i].to;
                double z=rpr[i].w;
                if(ch[u]+z<ch[v])
                {
                    ch[v]=ch[u]+z;
                    if(!bj[v])bj[v]=1,p.push(v);
                }
            }
        }
    }
    void A(){
        zz.d=1,zz.h=0,zz.f=0;
        q.push(zz);
        while(!q.empty())
        {
            zz=q.top();q.pop();
            if(zz.f>e)return;
            int u=zz.d;
            if(u==n)
            {
                e-=zz.f;ans++;continue;
            }
            for(int i=head[u];i;i=pr[i].n)
            {
                mm.d=pr[i].to;
                mm.h=zz.h+pr[i].w;
                mm.f=mm.h+ch[pr[i].to];
                q.push(mm);
            }
        }
    }
    int main()
    {
       cin>>n>>m;
        scanf("%lf",&e);
        for(int i=1;i<=m;i++)
        {
            int x,y;cin>>x>>y;
            double z;scanf("%lf",&z);
            add(x,y,z);
        }
        spfa();
        A();
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    Delphi高手突破第二章(1),堆与栈,构造与析构函数
    终于懂了:TWinControl与TCustomControl真正区别之处(TWinControl系统自绘,TCustomControl是Delphi自绘)
    Delphi高手突破第二章(3),封装,继承,多态
    不浪费自己一点一滴精力和体力在与自己目标不相干的事情上
    Delphi高手突破第二章(2),对象大小,类方法与类引用,错误的面向对象设计
    推荐微软Windows 8 Metro应用开发虚拟实验室
    利用TCP传输协议实现基于Socket的聊天程序
    持续集成
    Android 4.1 Surface系统变化说明
    自动化测试整理 STAF/STAX & Robot Framework
  • 原文地址:https://www.cnblogs.com/Uninstalllingyi/p/11214590.html
Copyright © 2020-2023  润新知