Astar
本页面将简要介绍 A*算法。
简介
A*搜索算法(英文:A*search algorithm,A*读作 A-star),简称 A*算法,是一种在图形平面上,对于有多个节点的路径求出最低通过成本的算法。它属于图遍历(英文:Graph traversal)和最佳优先搜索算法(英文:Best-first search),亦是 BFS 的改进。
定义起点 $s$,终点 $t$,从起点(初始状态)开始的距离函数 $g(x)$,到终点(最终状态)的距离函数 $h(x)$,$h^{\ast}(x)$[^note1],以及每个点的估价函数 $f(x)=g(x)+h(x)$。
A*算法每次从优先队列中取出一个 $f$ 最小的元素,然后更新相邻的状态。
如果 $h\leq h*$,则 A*算法能找到最优解。
上述条件下,如果 $h$ 满足三角形不等式,则 A*算法不会将重复结点加入队列。
当 $h=0$ 时,A*算法变为 Dijkstra;当 $h=0$ 并且边权为 $1$ 时变为 BFS。
例题
???+note "八数码" 题目大意:在 $3\times 3$ 的棋盘上,摆有八个棋子,每个棋子上标有 $1$ 至 $8$ 的某一数字。棋盘中留有一个空格,空格用 $0$ 来表示。空格周围的棋子可以移到空格中,这样原来的位置就会变成空格。给出一种初始布局和目标布局(为了使题目简单,设目标状态如下),找到一种从初始布局到目标布局最少步骤的移动方法。
```plain
123
804
765
```
??? note "解题思路" $h$ 函数可以定义为,不在应该在的位置的数字个数。
容易发现 $h$ 满足以上两个性质,此题可以使用 A\*算法求解。
??? note "参考代码"
cpp
--8<-- "docs/search/code/astar/astar_1.cpp"
???+note "k 短路" 按顺序求一个有向图上从结点 $s$ 到结点 $t$ 的所有路径最小的前任意多(不妨设为 $k$)个。
??? note "解题思路" 很容易发现,这个问题很容易转化成用 A*算法解决问题的标准程式。
初始状态为处于结点 $s$,最终状态为处于结点 $t$,距离函数为从 $s$ 到当前结点已经走过的距离,估价函数为从当前结点到结点 $t$ 至少要走过的距离,也就是当前结点到结点 $t$ 的最短路。
就这样,我们在预处理的时候反向建图,计算出结点 $t$ 到所有点的最短路,然后将初始状态塞入优先队列,每次取出 $f(x)=g(x)+h(x)$ 最小的一项,计算出其所连结点的信息并将其也塞入队列。当你第 $k$ 次走到结点 $t$ 时,也就算出了结点 $s$ 到结点 $t$ 的 $k$ 短路。
由于设计的距离函数和估价函数,每个状态需要存储两个参数,当前结点 $x$ 和已经走过的距离 $v$。
我们可以在此基础上加一点小优化:由于只需要求出第 $k$ 短路,所以当我们第 $k+1$ 次或以上走到该结点时,直接跳过该状态。因为前面的 $k$ 次走到这个点的时候肯定能因此构造出 $k$ 条路径,所以之后再加边更无必要。
??? note "参考代码"
cpp
--8<-- "docs/search/code/astar/astar_2.cpp"
参考资料与注释
[^note1]: 此处的 h 意为 heuristic。详见 启发式搜索 - 维基百科 和 A*search algorithm - Wikipedia 的 Bounded relaxation 一节。