1.题目
题目链接:点这儿!
2.解决方法
A*算法用来求高复杂度的最短路径问题;本题要求第K短路,这就要求将从起点到终点的所有路径中选出第K短的;我们不可能去把从起点到终点的所有路径一一找出来,那样复杂度太高了;所以我们采用启发式搜索的搜索模式:利用一个估价函数g()作为“启发函数”。
本题中将从终点出发的单源最短路作为估价函数值,这样,每个点在被从起点出发遍历时,都有一个预先知道的距离终点的最短路径长度,可以优先顺着这些点遍历,可以确定最终会遍历到终点;并且,不止一条路径会抵达终点,抵达的先后次序也就是路径的长短;至此,我们发现可以记录从起点向终点搜索的过程中每个点被遍历的次数,这个次数会不断更新;如果这个点是终点,那么记录的就是终点被遍历过的次数,当被更新了K次时,第K次遍历到终点的哪条路径就是第K短路径。
3.代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
//A*算法:先计算出每个点的估价函数(从终点向所有点扩散求最短路,dijkstra)此时估计函数值就是该点到终点的最短距离
//然后从起点开始,根据真实值+估价函数值做堆排序,这样能够保证队列中存储的是图中所有点到终点的最小估计值,以此扩散至终点
//A*算法的核心就是估价函数,本题中为在终点使用dijkstra得到的终点到其余所有点的最短距离dist
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
const int MAXN=1010,MAXM=2e5+10;
int h[MAXN],rh[MAXN],ne[MAXM],e[MAXM],w[MAXM],idx;
int n,m,a,b,l,S,T,K,cnt[MAXN];//cnt数组用来记录A*寻找第k短路中每个点被经过的次数
int dist[MAXN];
bool st[MAXN];
void add(int h[],int a,int b,int l)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=l;
h[a]=idx++;
}
void dijkstra()//从终点开始求一次单源最短路,在A*中做估价函数值
{
memset(dist,0x3f,sizeof(dist));
dist[T]=0;
priority_queue<PII,vector<PII>,greater<PII> > heap;
heap.push({0,T});
while(!heap.empty()){
auto t=heap.top();
heap.pop();
int ver=t.y,distance=t.x;
if(st[ver]) continue;
st[ver]=true;
for(int i=rh[ver];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>distance+w[i]){
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
}
int astar()
{
priority_queue<PIII,vector<PIII>,greater<PIII> > heap;
heap.push({dist[S],{0,S}});//{估价函数,{距起点的实际距离,点编号}} 估价函数=从终点dijkstra到每个点的最短路径+距起点的实际路径
while(!heap.empty()){
auto t=heap.top();
heap.pop();
int ver=t.y.y,distance=t.y.x;
cnt[ver]++;
if(cnt[T]==K) return distance;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(cnt[j]<K)
heap.push({distance+w[i]+dist[j],{distance+w[i],j}});
}
}
return -1;
}
int main(void)
{
cin>>n>>m;
memset(h,-1,sizeof(h));
memset(rh,-1,sizeof(rh));
for(int i=1;i<=m;i++){
cin>>a>>b>>l;
add(h,a,b,l);
add(rh,b,a,l);
}
cin>>S>>T>>K;
if(S==T) K++;
dijkstra();
cout<<astar()<<endl;
return 0;
}