题意:求一点至另一点的第K短路。注意如果起点和终点一样的话,那么刚开始起点不算到达终点。
分析:寻找最短路,直接SPFA就可以了。。。这里是第K短路,把每一条路径都找出来。开始想用DP,dp[i][j]表示到i结点第j大的路径权值是多少,但这样还是搜,而且会超时。。。所以还不如用A*搜,用优先队列每次将按照所走路径权值的最小值+此点到终点的最短距离的最小值出队列。
注意计算第几次到达某个点时是以出队列的时候算的,而不是入队列。开始求每个点到终点的最短路,建反向图,再用SPFA。。。
#include<stdio.h> #include<vector> #include<queue> #include<iostream> using namespace std; const int maxn=1100; const int maxm=110000; const int INF=0x3fffffff; struct edge { int u,v,w; }p0,p1; vector<edge>e[maxn],e1[maxn]; int dist[maxn],times[maxn],n,m,S,T,K; bool operator >(edge a,edge b) { return a.w+dist[a.v]>b.w+dist[b.v]; } priority_queue<edge,vector<edge>,greater<edge> >p; void SPFA(int s) { int i,j,k,head=0,tail=0,q[maxn*2]; bool inq[maxn]; for(i=1;i<=n;i++) dist[i]=INF,inq[i]=false; dist[s]=0,inq[s]=true; q[tail++]=s; while(head!=tail) { k=q[head]; inq[k]=false; head=(head+1)%(2*maxn); for(i=0;i<e1[k].size();i++) { j=e1[k][i].v; if(dist[j]>dist[k]+e1[k][i].w) { dist[j]=dist[k]+e1[k][i].w; if(!inq[j]) { inq[j]=true; q[tail]=j; tail=(tail+1)%(2*maxn); } } } } } void Search() { int i,now; bool tag=false; while(!p.empty()) p.pop(); memset(times,0,sizeof(times)); p0.v=S,p0.w=0; p.push(p0); while(!p.empty()) { p0=p.top(); p.pop(); if(++times[p0.v]>=K) { if(p0.v==T) { if(S!=T||(S==T&×[p0.v]==K+1))//如果起点和终点一样,那么一定要走才算一条路径 { tag=true; printf("%d\n",p0.w); break; } } if(times[p0.v]>K) continue; } now=p0.v; for(i=0;i<e[now].size();i++) { p1.v=e[now][i].v; p1.w=e[now][i].w+p0.w; p.push(p1); } } if(!tag) printf("-1\n"); } int main() { int i,j; while(scanf("%d%d",&n,&m)!=EOF) { for(i=1;i<=n;i++) { e1[i].clear(); e[i].clear(); } for(i=0;i<m;i++) { scanf("%d%d%d",&p0.u,&p0.v,&p0.w); e[p0.u].push_back(p0); j=p0.u; p0.u=p0.v; p0.v=j; e1[p0.u].push_back(p0); } scanf("%d%d%d",&S,&T,&K); SPFA(T); if(dist[S]==INF) { printf("-1\n"); continue; } Search(); } return 0; }