传送门
Dijkstra
这个题可以直接用 Dijkstra 来写。
A*
但还可以用 A* 来写。
就不深入学习 A* 了,下面只简单说说他的几个概念。
A* 的原理
跟 Dijkstra 着实很像。Dijkstra 每次选择一个估价函数最小的没有作为过出发点的结点,并用它去更新其他结点。这里,Dijkstra 的估价函数是起点到该结点的最短距离。注意,已经证明用这种方式去更新是保证正确的(保证求出来的是最短路),因此你尽管用。
A* 每次选择一个估价函数最小的没有作为过出发点的结点,并用它去更新其他结点。这里,A* 的估价函数是一个 。注意,已经证明用下面说的方式写出来的 是保证正确的(保证求出来的是最短路),因此你尽管用。很像吧。
A* 的
设 表示从起点到结点 的最短路距离。我们需要再写一个 ,然后令:
便得到了适用于 A* 的估价函数。
显然,当 时,这就是一个 Dijkstra,很简单吧?
A* 的
已经证明:
- 如果 ,其中 表示从 结点到终点的实际距离,则能得到最优解。
- 如果 ,那么搜索将严格沿着最短路径进行。
- 如果 ,不能保证得到最优解。
直观地说,当 越大,但又不超过 时,搜索时就更具方向性。让更有可能成为最短路上的点先出列,当然会更快。正确性?已经有人证明,我不管了。
另外需要注意, 时,不能保证得到最优解,但可能得到近似解。具体效率取决于你的 怎么写。(比如你写 怎么办?我不敢保证,我也不研究了)
于拯救行动
这道题的话,一个可行的能得到最优解的 显然可以是当前点到终点的曼哈顿距离,因为无论是遇到了墙堵路还是遇到了守卫堵路都会使得 。
用 A*,实测效率确实比用 Dijkstra 高了。赞!
参考代码
代码中还出现了两个概念:open(表)和 close(表)。怎么用的、什么意思,我不管了。用 Dijkstra 类比:close 表就是已经刷新过别的点的那些结点,open 表就是可以拿去刷新别的点的那些结点。Dijkstra 中为什么没有 close 表?因为 Dijkstra 用另一种方式代替了 close 表的存在(见我其他 Dijkstra 的代码),A* 中能这么写吗?我不敢保证,所以就用一个 close 表吧。
对了,代码中的 isInOpen 数组是不必要的,删除即可。
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
constexpr int maxn = 205;
int n, m;
char map[maxn][maxn];
int sx, sy, ex, ey;
int isInOpen[maxn][maxn];
int isInClose[maxn][maxn];
struct Node
{
int x, y;
int dis, pri;
Node() = default;
Node(int x, int y, int dis, int pri) :
x(x), y(y), dis(dis), pri(pri) {}
bool operator<(const Node& b) const
{
return pri > b.pri;
}
};
void AStar()
{
for (int i = 0; i < n; i++)
std::memset(isInOpen[i], 0, sizeof(isInOpen[0][0]) * m);
for (int i = 0; i < n; i++)
std::memset(isInClose[i], 0, sizeof(isInClose[0][0]) * m);
std::priority_queue<Node> open;
open.push(Node(sx, sy, 0, 0));
isInOpen[sx][sy] = true;
while (!open.empty())
{
auto from = open.top(); open.pop();
if (!isInOpen[from.x][from.y])
continue;
isInOpen[from.x][from.y] = false;
isInClose[from.x][from.y] = true;
if (from.x == ex && from.y == ey)
{
printf("%d\n", from.dis);
return;
}
const int vecx[]{ 1, -1, 0, 0 };
const int vecy[]{ 0, 0, 1, -1 };
for (int i = 0; i < 4; i++)
{
int newx = from.x + vecx[i];
int newy = from.y + vecy[i];
if (0 <= newx && newx < n && 0 <= newy && newy < m && map[newx][newy] != '#')
{
if (!isInClose[newx][newy])
{
open.push(Node(newx, newy, from.dis + 1 + (map[newx][newy] == 'x'),
[&](int x, int y, int g) -> int
{
int h = std::abs(ex - x) + std::abs(ey - y);
return g + h; // Dijkstra: h = 0
}(newx, newy, from.dis + 1 + (map[newx][newy] == 'x'))));
isInOpen[newx][newy] = true;
}
}
}
}
puts("Impossible");
}
int main()
{
int o;
std::cin >> o;
while (o--)
{
std::cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
char ch = std::getchar();
while (ch != '@' && ch != 'a' && ch != 'r' && ch != 'x' && ch != '#')
ch = std::getchar();
map[i][j] = ch;
if (ch == 'r')
{
sx = i;
sy = j;
}
else if (ch == 'a')
{
ex = i;
ey = j;
}
}
AStar();
}
return 0;
}
参考资料
只写我复制了字的资料: