求第k最短路问题,有向图,数据范围:点数不超过1000,边数不超过10^5,k不超过1000
题目需要注意的地方:虽然题目描述说的是求T到S的第k短路,但是实际上根据样例可以知道我们应该求从S到T的第k短路...另外如果S==T,最初的那个不用走就到了的是不算做一条路的..也就是说必须要走
算法:首先用原图的反向图计算每个点到T的最短路,然后使用A*算法,估价函数f(i)=g(i)+h(i),其中h(i)是从i到T的最短路长度,g(i)是目前从起点到i所搜索出来的某条路径的长度。使用优先队列,每次选取f(i)最小的点进行扩展。一个点可以在同一时刻在队列里出现多次,因为他们代表不从S按照不同的路径到达这个点。
空间复杂度:队列的大小不会超过nk,因为如果某个点在队列里累计出现了k+1次,那么从k+1次的那个点向外扩展到t,最少是第k+1短路而不是k短路
时间复杂度:会遍历到从S点开始所有长度小于第k短路的有希望到达T点的路,若这些路上的点的和为t,则复杂度为tlogt。因为每个点都不会进入队列超过k次,所以复杂度不会超过nklog(nk)
需要注意的地方:只有出队序列才是满足估价函数逐渐增长的,不可以按照入队序列查看,T点的第k次出队才是第k短路。如果某个点不能够到达T点,那么我们不会将其加入优先队列。若队列为空,则不存在第k短路。
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct Node {
int fe,v;
bool visited;
};
struct Edge {
int ne,t,v;
};
struct Que {
int i,f;
Que() {}
Que(int ii,int ff) {
i=ii;f=ff;
}
friend bool operator < (const Que &a,const Que &b) {
return a.f>b.f;
}
};
int n,m,k,s,t,p,q,ansk;
Node a[1001];
Edge b[100001];
Node c[1001];
Edge d[100001];
int que[1001];
priority_queue <Que> e;
void putedge(int x,int y,int z) {
b[p].ne=a[x].fe;
b[p].t=y;
b[p].v=z;
a[x].fe=p;
d[p].ne=c[y].fe;
d[p].t=x;
d[p].v=z;
c[y].fe=p;
p++;
}
void spfa(int s,int t,Node a[],Edge b[]) {
int i,j;
p=q=0;
a[s].v=1;
a[s].visited=true;
que[q++]=s;
while (p!=q) {
i=que[p];
for (j=a[i].fe;j;j=b[j].ne) {
if (a[b[j].t].v==0||a[b[j].t].v>a[i].v+b[j].v) {
a[b[j].t].v=a[i].v+b[j].v;
if (!a[b[j].t].visited) {
a[b[j].t].visited=true;
que[q]=b[j].t;
q=(q+1)%1001;
}
}
}
a[i].visited=false;
p=(p+1)%1001;
}
}
int astar(int s,int t,Node a[],Edge b[]) {
e.push(Que(s,a[s].v));
while (!e.empty()) {
int i=e.top().i;
int v=e.top().f;
//printf("%d %d\n",i,v-a[i].v);
e.pop();
if (i==t) {
ansk++;
if (k==ansk) return v;
}
for (int j=a[i].fe;j;j=b[j].ne) {
if (a[b[j].t].v>=0) {
e.push(Que(b[j].t,v+b[j].v+a[b[j].t].v-a[i].v));
//printf("Push: %d %d\n",b[j].t,v-a[i].v+b[j].v);
}
}
}
return -1;
}
int main() {
int i,x,y,z;
scanf("%d%d",&n,&m);
p=1;
ansk=0;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
for (i=0;i<m;i++) {
scanf("%d%d%d",&x,&y,&z);
putedge(x,y,z);
}
scanf("%d%d%d",&s,&t,&k);
if (s==t) k++;
spfa(t,s,c,d);
for (i=1;i<=n;i++) a[i].v=c[i].v-1;
//for (i=1;i<=n;i++) printf("%d ",a[i].v);
//printf("\n");
if (a[s].v!=-1) {
printf("%d\n",astar(s,t,a,b));
} else printf("-1\n");
return 0;
}