第k短路(A*) (参考的一个acm模板,不知道挂啥链接)
A*算法思想
- 广度优先搜索:以广度做为优先级进行搜索。从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点,时间空间开销大。
- 深度优先搜索:每次沿着特定规则走,纵向寻找答案,找到答案的时机视具体情况而不同。
对比:假如深度优先在搜索的时候恰好第一次搜索就是正确路径,这个效率就很高,而广度优先是不做决策,同步进行。那么我们有没有办法使得算法在搜索的时候有目的的去搜索呢?答案是肯定的。
启发式搜索(A*)
给出一个估价函数:g(n) = f(n) + h(n),构造一个最小堆。
- g(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
- f(n) 是节点n距离起点的代价。
- h(n)是节点n距离终点的预计代价。
启发函数
- 在极端情况下,当启发函数h(n)始终为0,则将由f(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
- 如果h(n)始终小于等于节点n到终点的代价,则A*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
- 如果h(n)完全等于节点n到终点的代价,则A*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
- 如果h(n)的值比节点n到终点的代价要大,则A*算法不能保证找到最短路径,不过此时会很快。
- 在另外一个极端情况下,如果h(n)相较于f(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。
每次找到终点计数器加一,在第k次到达终点时(出堆的结点id为终点)即找到了答案。
邻接矩阵
/*==================================================*\
| 第 K 短路(A*)
| A* 估价函数 fi为到当前点走过的路经长度, hi为该点到终点的长度
| gi = hi + fi;
\*==================================================*/
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
using namespace std;
int n, m, k;
int g[1010][1010], gr[1010][1010];
int dist[1010], v[1010]; //dist[i] : i到n-1的最短路长, v[i]:标记一访问
const int INF = 1000000000;
struct node{
int id, fi, gi;
node (){};
node (int _id, int _fi, int _gi) : id(_id), fi(_fi), gi(_gi) {};
friend bool operator <(const node& a, const node& b){
if (a.gi == b.gi) return a.fi > b.fi; //优先走到最近的
return a.gi > b.gi; //优先总路径最小
}
};
priority_queue<node> q;
int init(){ //反向dijikstra求出dist[]
for (int i = 0; i <= n; i++){
dist[i] = INF;
v[i] = 1;
}
dist[n-1] = 0; //从终点n-1出发
for (int i = 0; i < n; i++){
int k = n;
for (int j = 0; j < n; j++)
if (v[j] && dist[j] < dist[k]) k = j;
if (k == n) break;
v[k] = 0;
for (int j = 0; j < n; j++)
if (v[j] && dist[k] + gr[k][j] < dist[j])
dist[j] = dist[k] + gr[k][j];
}
return 1;
}
int solve(){
if (dist[0] == INF) return -1;
int cnt = 0; //第几次找到最短路路径
q.push(node(0, 0, dist[0]));
while (!q.empty()){
node cur = q.top();
q.pop();
if (cur.id == n-1) cnt++;
if (cnt == k) return cur.fi;
for (int i = 0; i < n; ++i)
if (g[cur.id][i] != INF)
q.push(node(i, cur.fi + g[cur.id][i], dist[i]));
}
return -1;
}
int main(){
while (scanf("%d %d %d", &n, &m, &k) != EOF){
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
gr[i][j] = g[i][j] = INF;
for (int i = 0; i < m; i++){
int x, y, z;
scanf("%d %d %d",&x, &y, &z);
x--, y--;
g[x][y] = z;
gr[y][x] = z; //反向建图
}
init();
printf("%d\n", solve());
}
return 0;
}