题意:在一个有向图上求第K短路。
如果在一个边权均为1的有向图上BFS,那么第一个搜索到的解就是最短路,第k个搜索到的解就是第k短路。
当边权不均为1时,搜索时每次要扩展的节点是谁就成为了问题。显然,我们想优先扩展的应该是能使解最接近最短路的节点,而这样的节点到终点最短距离加上起点到它距离应该是当前可扩展出的节点中最小的。起点到节点距离可以在搜索时记录下来,节点到终点最短距离可以在原图的反向图上求最短路得到。
K短路也是A*算法的典例。搜索时每次优先扩展的就是消耗代价与当前到目标估计代价之和最小的节点,而在这个问题中,估计值就等于实际值,这保证我们可以用很高的效率正确的在、搜索到第K短的路径。
搜索时若一个点已被访问了k次以上,那么这个点就可以跳过了。同时注意起点与终点相同时k++。
做BFS时我们用队列保存节点,那么A*只要用优先队列保存几点即可。
#include<cstdio> #include<cstring> #include<iostream> #include<queue> using namespace std; const int maxn=3010; const int maxm=100010; const int INF=0x3f3f3f3f; struct edge{ int y,next,w; } e1[maxm],e2[maxm]; int head1[maxn],head2[maxn],tot=0; /*1为正向图,2为反向图*/ int dis[maxn],cnt[maxn]; int m,n,k,S,T; bool vst[maxn]; typedef pair<int,int>pii; priority_queue<pii,vector<pii>,greater<pii> >q;/*Dijsktra用*/ struct node{ int d,h,p; }; class cmp{ public: bool operator ()(node a,node b){ return a.d+a.h>b.d+b.h; } }; priority_queue<node,vector<node>,cmp> q2;/*搜索用*/ void add(int x,int y,int w){ tot++; e1[tot].y=y; e1[tot].next=head1[x]; e1[tot].w=w; head1[x]=tot; e2[tot].y=x; e2[tot].next=head2[y]; e2[tot].w=w; head2[y]=tot; } void dijsktra(){ for(int i=1;i<=n;i++)dis[i]=i==T?0:INF; memset(vst,0,sizeof(vst)); q.push(make_pair(dis[T],T)); while(!q.empty()){ int x=q.top().second; q.pop(); if(vst[x])continue; vst[x]=1; for(int i=head2[x];i;i=e2[i].next){ int y=e2[i].y; int w=e2[i].w; if(dis[y]>dis[x]+w){ dis[y]=dis[x]+w; q.push(make_pair(dis[y],y)); } } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y,w; scanf("%d%d%d",&x,&y,&w); add(x,y,w); } scanf("%d%d%d",&S,&T,&k); if(S==T)k++;/*深坑在此*/ dijsktra(); node x; x.d=0; x.h=dis[S]; x.p=S; q2.push(x); memset(cnt,0,sizeof(cnt)); while(!q2.empty()){ int x=q2.top().p; int d=q2.top().d; q2.pop(); if(++cnt[x] > k)continue; if(cnt[T] == k){ printf("%d\n",d); return 0; } for(int i=head1[x];i;i=e1[i].next){ int y=e1[i].y; int w=e1[i].w; node tmp; tmp.d=d+w; tmp.h=dis[y]; tmp.p=y; q2.push(tmp); } } printf("-1\n"); }