搜索 —— 启发式搜索 —— A* 算法
【概述】
A*(A-Star)算法是一種在靜態(tài)路網(wǎng)中,求解最短路的最有效的直接搜索方法,也是解決許多搜索問題的有效算法之一。
A* 算法實(shí)際上是對 Dijkstra 算法的優(yōu)化后得到的,關(guān)于 Dijkstra 算法:點(diǎn)擊這里
A*算法在程序設(shè)計(jì)競賽中,一般用于解決 k 短路問題,關(guān)于 k 短路問題:點(diǎn)擊這里
【原理】
在 Dijkstra 算法中,我們借助優(yōu)先隊(duì)列來實(shí)現(xiàn),每次從優(yōu)先隊(duì)列中取出結(jié)點(diǎn),再從這個結(jié)點(diǎn)擴(kuò)散到其他結(jié)點(diǎn),而優(yōu)先隊(duì)列的優(yōu)先依據(jù)是根據(jù)起點(diǎn)到隊(duì)列中結(jié)點(diǎn)最近的一個,即隊(duì)列中的結(jié)點(diǎn)離起始點(diǎn)最近的會先出隊(duì)。
但不可避免的是,Dijkstra 存在跑偏問題,如下圖,若起點(diǎn)為 1 號點(diǎn),終點(diǎn)為 6 號點(diǎn),首先一定會走 3、4 號點(diǎn),這肯定不是最佳路徑,一開始就跑偏了。
在 Dijkstra 算法中,判斷的依據(jù)只是已知的起點(diǎn)到某點(diǎn) i 的距離 G(i),那么我們可以增加一個判斷量來減少跑偏的概率。
如果能夠計(jì)算當(dāng)前點(diǎn) i 到終點(diǎn)的距離,再結(jié)合 G(i),那么可以防止過度跑偏,但這個距離是未知的,因此我們可以設(shè)置一個估值,即啟發(fā)函數(shù) H(i)。
啟發(fā)函數(shù) H(i) 的設(shè)置是 A* 算法關(guān)鍵,如何設(shè)置這個啟發(fā)函數(shù)影響到算法的性能,在得到了啟發(fā)函數(shù)后,我們就有了最終的估價函數(shù) F(i)=G(i)+H(i),在優(yōu)先隊(duì)列中,以這個估價函數(shù)作為指標(biāo)進(jìn)行出隊(duì)。
一般來說,在二維平面圖中,我們將圖置于坐標(biāo)軸中,通過計(jì)算結(jié)點(diǎn)間的歐幾里得距離來得到兩點(diǎn)間的直線距離,這個距離即可作為當(dāng)前點(diǎn) i 到終點(diǎn)距離的估值。但歐幾里得距離的計(jì)算要進(jìn)行開方,較為復(fù)雜,一般通過加減法來計(jì)算簡單的曼哈頓距離來代替歐幾里得距離,作為啟發(fā)函數(shù) H(i)。
而在一般圖中,我們通常是建立反圖,跑一次最短路算法,將得到每個點(diǎn)到 t?的最短路的距離 dis[i] 作為啟發(fā)函數(shù) H(i)
【實(shí)現(xiàn)】
struct Edge {int to, next;int w;Edge() {}Edge(int to, int next, int w) : to(to), next(next), w(w) {} }; struct Map {int tot;int head[N];Edge edge[N * N];Map() {tot = 0;memset(head, -1, sizeof(head));}void addEdge(int x, int y, int w) {edge[++tot].to = y;edge[tot].next = head[x];edge[tot].w = w;head[x] = tot;}Edge &operator[](int pos) { return edge[pos]; } }; int n, m; Map G, GT; int dis[N]; bool vis[N]; struct Status{int node;//點(diǎn)編號int diss;//距離int priority;//優(yōu)先級Status() : node(0), diss(0), priority(dis[0]) {}Status(int node, int diss) : node(node), diss(diss), priority(diss + dis[node]) {}bool operator<(Status b) const { return priority > b.priority; } } status; bool SPFA(int S, int T) { //對反圖求最短路memset(dis, INF, sizeof(dis));memset(vis, false, sizeof(vis));dis[S] = 0;queue<int> Q;Q.push(S);while (!Q.empty()) {int x = Q.front();Q.pop();vis[x] = false;for (int i = GT.head[x]; i != -1; i = GT[i].next) {int y = GT[i].to;if (dis[x] + GT[i].w < dis[y]) {dis[y] = dis[x] + GT[i].w;if (!vis[y]) {Q.push(y);vis[y] = true;}}}}return dis[T] != INF; } int AStar(int S, int T) {priority_queue<Status> Q;Q.push(Status(S, 0));while (!Q.empty()) {Status temp = Q.top();Q.pop();if (temp.node == T)return temp.diss;for (int i = G.head[temp.node]; i != -1; i = G[i].next)if (temp.diss + G[i].w < temp.diss)Q.push(Status(G[i].to, temp.diss + G[i].w));}return -1; } int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= m; i++) {int x, y, w;scanf("%d%d%d", &x, &y, &w);G.addEdge(x, y, w);GT.addEdge(y, x, w);}int s, t;scanf("%d%d", &s, &t);if (!SPFA(t, s))printf("-1\n");elseprintf("%d\n", AStar(s, t));return 0; }?
總結(jié)
以上是生活随笔為你收集整理的搜索 —— 启发式搜索 —— A* 算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天堂里的游戏(51Nod-1417)
- 下一篇: 树形结构 —— 树与二叉树 —— 树的直