题目大意就是给出起点s,终点t和一个参数k,以及一张图,问s到t间的k短路。
对于所有k短路问题,我们都可以用最短路+A*的方法来解决。
首先,我们根据输入数据建两张图:一张是和输入数据边的方向相同的正图,另一张是和输入数据边的方向相反的反图。
我们对反图以终点t为起点做一遍最短路(我用的是SPFA),可以得到所有点到终点t的最短距离。
然后从起点s开始做一遍A*,第k次遍历到终点t时的路径长度就是答案。
其中我们定义A*的估价函数f(i)=g(i)+h(i),h(i)的大小就是最短路得出来的节点i到终点t的最短路径长度,g(i)是原点s到节点i的实际距离。
为了使f较小的节点会被较早的扩展,保持BFS的特性,我们可以用一个优先队列来维护,以f为关键字。
因为我们每次取队头节点p,也就是整个队列中f最小的节点,所以当前状态是队列里所有节点p的关键字f最小的状态,在把所有与节点p相连的节点插入队列,相当于枚举了所有节点的关键字f。每次取的都是所有状态中关键字f最小的状态,保证了每次枚举到的节点都是以最短路径扩展而来。因此可以推出用A*的方法得到的k短路是成立的。
最后一次搜到终点t时,g(t)就是答案。
注意:如果s=t,则++k。这个……我也不知道为何……
附上AC代码:
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
struct note{
int d,w;
};
vector <note> z[1010],f[1010];
int n,m,x,y,w,s,t,k,dis[1010];
queue <int> que;
bool b[1010];
struct node{
int v,g,f;
bool operator < (const node lyf) const {
if (lyf.f==f) return lyf.g<g;
return lyf.f<f;
}
};
priority_queue <node> dui;
void spfa(){
for (int i=1; i<=n; ++i) dis[i]=2e9;
que.push(t),b[t]=1,dis[t]=0;
while (!que.empty()){
int p=que.front();que.pop(),b[p]=0;
for (int i=0; i<f[p].size(); ++i)
if (dis[f[p][i].d]>dis[p]+f[p][i].w){
dis[f[p][i].d]=dis[p]+f[p][i].w;
if (!b[f[p][i].d]){
b[f[p][i].d]=1;
que.push(f[p][i].d);
}
}
}
return;
}
int astar(){
if (s==t) ++k;
if (dis[s]==2e9) return -1;
int c=0;
dui.push((node){s,0,dis[s]});
while (!dui.empty()){
node p=dui.top();dui.pop();
if (p.v==t) ++c;
if (c==k) return p.g;
for (int i=0; i<z[p.v].size(); ++i)
dui.push((node){z[p.v][i].d,p.g+z[p.v][i].w,p.g+z[p.v][i].w+dis[z[p.v][i].d]});
}
return -1;
}
int main(void){
scanf("%d%d",&n,&m);
for (int i=1; i<=m; ++i){
scanf("%d%d%d",&x,&y,&w);
z[x].push_back((note){y,w});
f[y].push_back((note){x,w});
}
scanf("%d%d%d",&s,&t,&k);
spfa();
printf("%d",astar());
return 0;
}