【C++】A*算法

210 阅读2分钟

「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」。

算法简介

AA^*(A starA\ star)算法是一种启发式搜索算法,主要实现对图的路径搜索。AA^*算法基于BFSBFS(广度优先搜索),由于BFSBFS具有盲目性,会进行许多偏离最佳路径的搜索,故此AA^*算法主要目的就是克服BFSBFS的盲目性,在进行光度搜索时有目的的选择搜索结点。

算法思想

在学习AA^*算法之前,首先需要引入评估函数f(n)=h(n)+g(n)f(n)=h(n)+g(n),它用于评估搜索点nn的优先级。

其中,f(n)f(n)是搜索点nn的优先级,f(n)f(n)越小优先级越高。h(n)h(n)表示从起点到点nn的已花费代价。g(n)g(n)表示点nn到终点的估计代价。

回顾BFSBFS

BFSBFS是广度优先搜索,他通常采用一个队列,每次从队首读入访问一个点,并把他连接的为访问点放入队尾,直到队列为空。BFSBFS的模板如下:

void BFS(int k){
    queue<int> Q;
    Q.push(k);//将点k入队
    visit[k]=1;//标记已访问k
    while(!Q.empty()){
        int u=Q.front();Q.pop();//出队
        visit[u]=1;//标记u已访问
        if(u==end){//找到终点
            //得到结果的相关操作;
        }
        for(int i=head[u];i;i=last[i]){//访问u连接的结点
            int v=to[i];//v是u连接的结点编号
            if(!visit[v]){
                Q.push(v);
            }
        }
    }
}

引入AA^*

AA^*算法所做的工作就是修改队列的逻辑,因为我们要按优先级访问,所以需要使用优先队列。在C++C++当中,可以直接用priority_queuepriority\_queue优先队列,存入顶点可以用pair<pri,node>pair<pri,node>来存取,pripri表示优先级,nodenode表示顶点编号,因为pairpair默认比较逻辑是按照第一个值来比较,所以第一个应该是优先级。

void Astar(int k){
    typedef pair<int,int> pir;
    priority_queue<pir,vector<pir>,greater<pir> > Q;//因为优先队列默认是大根堆,所以要修改一下比较逻辑
    Q.push(make_pair(0,k));//将点k入队,优先级0最高
    visit[k]=1;//标记已访问k
    while(!Q.empty()){
        int u=Q.front().first;Q.pop();//出堆
        visit[u]=1;//标记u已访问
        if(u==end){//找到终点
            //得到结果的相关操作;
        }
        for(int i=head[u];i;i=last[i]){//访问u连接的结点
            int v=to[i];//v是u连接的结点编号
            if(!visit[v]){
                Q.push(make_pair(f(v)+g(v),v));//v和v的评估入堆
            }
        }
    }
}

关于h(n)h(n)g(n)g(n)

一般来说,h(n)h(n)评估起点到nn的距离可以通过搜索过程计算出来,g(n)g(n)到终点的距离可以通过反向DijkstraSPFADijkstra、SPFA等等单源最短路径算法作为参考值。更一般的,可以使用曼哈顿距离、对角距离、欧氏距离等等来评估。

应用

部分游戏的寻路算法就是用了AA^*,除此之外,AA^*还可以用来求解kk短路问题,当第kk次搜索找到终点时,即为kk短路。但在一般情况下,AA^*只能找到较优解,而不一定是最优解。