给定一张N个点(编号1,2…N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边。
注意: 每条最短路中至少要包含一条边。
输入格式
第一行包含两个整数N和M。
接下来M行,每行包含三个整数A,B和L,表示点A与点B之间存在有向边,且边长为L。
最后一行包含三个整数S,T和K,分别表示起点S,终点T和第K短路。
输出格式
输出占一行,包含一个整数,表示第K短路的长度,如果第K短路不存在,则输出“-1”。
数据范围
1≤S,T≤N≤1000
0≤M≤1e5
1≤K≤1000
1≤L≤100
输入样例:
2 2
1 2 5
2 1 4
1 2 2
输出样例:
14
思路:A*算法。这里含有一个估算成本,即某个点到终点的估计距离h,若设这个点到起点的距离为d,到终点的实际距离为g,则这里要保证h<=g。以h+d为依据排序更新最短路会比直接更新快许多。 当h==0的时候,这个算法会退化为迪杰斯特拉算法,当h==g时,这个算法会进化成线性算法。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 1e9+7
using namespace std;
const int maxn = 1e3+10,mod=1e9+7;
int n,m,S,T,K;
int dist[maxn],cnt[maxn];
//dist数组用于记录每个点到终点的距离,即A*算法中的估算成本
bool st[maxn];
struct dd{
int to,w;
//to为这条边的终点,w为这条边的长度
};
vector<dd>q[maxn],qa[maxn];//存正向图和反向图
struct node{
int all,diss,id;
//all为整条线路的长度
//diss为这个点到起点的距离
//id为这个点的下标
};
bool operator < (node x,node y){
return x.all>y.all;
//优先队列中以整条线路的长度排序
}
void dijkstra(int start){
//平平无奇的dijkstra算法
//由于求的是终点到其他点的最短距离,所以用反向图
memset(dist,inf,sizeof(dist));
dist[start]=0;
int u,len;
while(1){
u=0;
for(int i = 1; i <= n; i++)
if(!st[i])
if(u==0||dist[u]>dist[i])u=i;
if(u==0)break;
st[u]=true;
len=qa[u].size();
for(int i = 0; i < len; i++)
dist[qa[u][i].to]=min(dist[u]+qa[u][i].w,dist[qa[u][i].to]);
}
}
void astar(){
priority_queue<node>que;
if(S==T)K++;//路中至少包含一条边
que.push({dist[S],0,S});//讲起点放入优先队列中
node u;dd v;
int len,distance;
//广搜过程
while(!que.empty()){
u=que.top(),que.pop();
cnt[u.id]++;//统计每个点经过的次数
//求得答案则直接输出
if(u.id==T&&cnt[T]==K){
printf("%d\n",u.diss);
return;
}
//将u点能到达的点推入优先队列中
distance=u.diss,len=q[u.id].size();
for(int i = 0; i < len; i++){
v=q[u.id][i];
que.push({distance+v.w+dist[v.to],distance+v.w,v.to});
}
}
//若是不能经过终点K次,则输出-1
printf("-1\n");
}
int main(){
scanf("%d %d",&n,&m);
int x,y,z;
for(int i = 1; i <= m; i++){
scanf("%d %d %d",&x,&y,&z);
q[x].push_back({y,z});//正向图
qa[y].push_back({x,z});//反向图
}
scanf("%d %d %d",&S,&T,&K);
dijkstra(T);
astar();
return 0;
}