题目大意
给定 n个点 m条边 给定起点终点和k
求起点到终点的第k短的路径是多少 没有就输出-1
样例
Sample Input
2 2
1 2 5
2 1 4
1 2 2
Sample Output
14
思路
根据估价函数的设计准则,在第K短路中从x到T的估计距离f(x)应该不大于第K短路中从x到T的实际距离g(x)。 于是,我们可以把估价函数f(x) 定为从某个点到T的最短路长度,这样不但能保证f(x)≤g(x),还能顺应g(x) 的实际变化趋势,最终我们得到了以下A*算法:
1.预处理出各个节点x到终点T的最短路长度f(x) 一这等价于在反向图上T为起点求解单源最短路径问题,可以在0((N + M) log(N + M))的时间内完成。
2.建立一个二叉堆,存储些二元组(x, dist+f(x) ), 其中x为节点编号,dist表示从S到x当前走过的距离。起初堆中只有(S,0 + f(0))。
3.从二叉堆中取出dist +f(x)值最小的二元组(x,dist +(f(x) ),然后沿着从x出发的每条边(x,y)进行扩展。如果节点y被取出的次数尚未达到K,就把新的二元组(y,dist + length(x,y) + f(y))插入堆中。
4.重复第2~3步,直至第K次取出包含终点T的二元组,此时二元组中的dist值就是从S到T的第K短路。
A_star 算法的复杂度上界与优先队列BFS相同。不过因为估价函数的作用,图中很多节点访问次数都远小于K,上述A* 算法已经能够比较快速地求出结果.
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
const int N = 2010, M = 200010;
int ne[M], dis[N], head[N], rhead[N], e[M], w[M], len;
struct node{
int first, second;
bool operator<(const node &x) const{
return first > x.first;
}
};
struct node2{
int a, b, c;
bool operator<(const node2 &x) const{
return a > x.a;
}
};
int n, m, S, T, K;
int vis[N];
void add(int *h, int u, int v, int z){
e[len] = v;
w[len] = z;
ne[len] = h[u];
h[u] = len++;
}
void dijkstra(){
memset(dis, 0x3f, sizeof dis);
priority_queue<node> q;
q.push({0, T});
dis[T] = 0;
while(q.size()){
node t = q.top();
q.pop();
int u = t.second;
if(vis[u]) continue;
vis[u] = 1;
for(int i = rhead[u]; ~i; i = ne[i]){
int v = e[i];
if(dis[v] > dis[u] + w[i]){
dis[v] = dis[u] + w[i];
q.push({dis[v], v});
}
}
}
}
int a_star(){
memset(vis, 0, sizeof vis);
priority_queue<node2> q;
q.push({dis[S], 0, S});
while(q.size()){
node2 t = q.top();
q.pop();
int u = t.c;
int distance = t.b;
vis[u]++;
if(u == T && vis[u] == K) return distance;
for(int i = head[u]; ~i; i = ne[i]){
int v = e[i];
q.push({dis[v] + distance + w[i], distance + w[i], v});
}
}
return -1;
}
int main(){
scanf("%d%d", &n, &m);
memset(head, -1, sizeof head);
memset(rhead, -1, sizeof rhead);
for(int i = 0; i < m; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(head, a, b, c);
add(rhead, b, a, c);
}
scanf("%d%d%d", &S, &T, &K);
if(S == T) K++;
dijkstra();
printf("%d\n", a_star());
return 0;
}