图论1-k短路
相信大家都已经会求最短路了,k短路是在最短路熟练掌握的情况下进行的一个进阶,要求把最短路的过程想的十分通透才能学的懂。
先摆出一道例题做引例:
http://poj.org/problem?id=2449 POJ2449 Remmarguts' Date
大概题意就是求从s到t的第k短路,注意要反着建边。同时如果s==t,什么都不走的这条路不算,也就是if(s==t) k++;
如果大家题都读懂了就往下看。
这是一个十分令人困惑的题,之前学的最短路算法都只能记录一个dis,也就是从s到这个点的最短距离,可我们这次要求的k短路,
我们该怎么办呢?因为SPFA是从bfs扩展出来的,所以我们先从bfs想,如果是bfs的话,那第k短路就是第k次访问t时的路径,但是
SPFA中没法这样做啊,所以要新开一个函数来做这件事。但是能bfs的条件是所有边得权值都一样,但不一样怎么办呢?我们必须
要先扩展代价较小的,但如何确定哪个点的代价小呢?我们必须要一个估价函数,也就是A*(A star),这个估价函数必须要满足
比真实代价小。但是这个估价该怎么算呢,我们会发现可以由现有代价加上这个点到t点的代价就可以是它的估价,满足成为估价的
条件,大概的思路已经理清楚了。现在,我在串一遍整个思路。
1.读入,建图。注意要正反图都要建,因为我们算任意一点到s要建反图,但是A*时要正图,所以我们正反图都要建。
2.反图跑一遍spfa,初始化出所有点到s的代价,以便估价函数运算。
3.正图A*,并找出答案,每个结构体有三个量,分别是x,ans,f,表示当前节点编号,答案,以及估价,priority_queue按估价升序排序即可。
注意:
1.当s==t是k要++.
2.记得输出-1
3.记得要建两个图
下面就是代码了:
#include<iostream> #include<cstring> #include<queue> using namespace std; const int NR=1e5+10; const int INF=0x3f3f3f3f;//极大值 int n,m; int dis[NR];//算估价时要用,用每个点到t的最小值 int tot;//边的总数 int head1[NR],head2[NR];//正反两个图 bool flag[NR];//spfa的时候判断是否在队列里 struct edge { int to; int w; int next; }e[NR<<1],e2[NR<<1];//邻接表正常操作 struct Nd { int f,ans,x; bool operator < (Nd A)const//按估价升序排列,如果估价相等就看真实的代价 { if(A.f==f) return ans>A.ans; return f>A.f; } }; void add(int u,int v,int w) { tot++; e[tot].to=v; e[tot].w=w; e[tot].next=head1[u]; head1[u]=tot; e2[tot].to=u; e2[tot].w=w; e2[tot].next=head2[v]; head2[v]=tot; //两遍建图 } void spfa(int t)//默认spfa会打 { for(int i=1;i<=n;i++) dis[i]=INF; dis[t]=0; queue<int> q; q.push(t); flag[t]=1; while(!q.empty()) { int x=q.front(); q.pop();flag[x]=0; for(int i=head2[x];i;i=e2[i].next) { int y=e2[i].to,w=e2[i].w; if(dis[y]>dis[x]+w) { dis[y]=dis[x]+w; if(!flag[y]) { q.push(y); flag[y]=1; } } } } } int a_star(int s,int t,int k)//进入A*函数 { if(s==t) k++;//如果起终点相同,不能站着不动,就是要去掉一种所以k++ if(dis[s]==INF) return -1;//如果都到不了,就没有扩展的必要了 priority_queue<Nd> q; int cnt=0;Nd x,to; x.x=s;x.ans=0;//初值 x.f=x.ans+dis[x.x];//计算出使估价 q.push(x); while(!q.empty()) { x=q.top();q.pop(); if(x.x==t) cnt++;//每一次经过终点,就找到了一条路,所以cnt要++ if(cnt==k) return x.ans;//如果找到第k短路了,结束A*函数 for(int i=head1[x.x];i;i=e[i].next) { to.x=e[i].to; to.ans=x.ans+e[i].w; to.f=to.ans+dis[to.x]; q.push(to); //每次扩展 } } return -1;//没有第k短路输出-1 } int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*f; } int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); add(x,y,z);//建图 } int s=read(),t=read(),k=read(); spfa(t);//从t开始跑最短路 int ans=a_star(s,t,k);//得到答案 printf("%d",ans); return 0; }