传送门:Remmarguts' Date
题意为求源点 s 到 终点 t 的第 k 短路,可以采用启发式搜索 A* 算法来求解。
A* 算法的核心为估值函数 f(x) = g(x)+h(x) ,其中 f(x) 为当前某个点的估值,g(x) 为开始搜索时到当前点的代价,h(x) 为当前点到目标点的代价的估值,h(x) 必须小于等于实际值。而在本题中,g(x) 为到达当前点时已走的路径长度,h(x) 为当前点到目标点的最短路径长度。
具体的求解方法是,首先利用 SPFA 求解反向最短路。估值函数中的 h(x) 采用了当前点到目标点的最短路径长度,由于题目给定的图是有向图,所以可以在建图时同时反向建图(即建立一个所有边方向相反权重不变的图),然后从终点 t 开始做一次单源最短路,求出 t 到其他点的最短路长。这样,h(x) 的值就确定下来了。
然后就可以进行 A* 算法。A* 算法类似 BFS的优化(实际上,可以通过改变估值函数,使得 A* 算法退化为 BFS),它利用一个优先队列维护 f(x),使得估值 f(x) 越小,则越早出队拓展。出队的点与 BFS 一样,访问与该点有边相连且为边指向的点,更新状态(估值函数),加入优先队列。当优先队列出队的点为 t 时,若为 t 第一次出现,说明这是 s 到 k 的最短路;若为第二次出现,则说明这是第 2 短路...以此类推,当 t 第 k 次出队时,求出的便是 k 短路了。
另外,需要注意的是,若 s == t,需要执行一个 ++k 的操作...因为作为起点的 t 会多出队一次。
#include <iostream>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1024;
const int maxm = 1e5+5;
struct Edge
{
int to, w, next;
Edge() {}
Edge(int to, int w, int next): to(to), w(w), next(next) {}
};
struct Node
{
int x, g, h;
Node() {}
Node(int x, int g, int h): x(x), g(g), h(h) {}
bool operator < (const Node& n) const //注意这里的符号别写反...否则会无限MLE...
{
if((g+h) != (n.g+n.h))
return (g+h) > (n.g+n.h);
return g > n.g;
}
};
int n, m, s, t, k;
Edge edge1[maxm], edge2[maxm];
int head1[maxn], head2[maxn];
int dis[maxn], vis[maxn];
void init()
{
for(int i = 0; i < maxn; ++i)
head1[i] = head2[i] = -1;
}
void addEdge(Edge edge[], int head[], int u, int v, int w, int id)
{
edge[id].w = w;
edge[id].to = v;
edge[id].next = head[u];
head[u] = id;
}
void read()
{
cin >> n >> m;
int u, v, w;
for(int i = 0; i < m; ++i)
{
cin >> u >> v >> w;
addEdge(edge1, head1, u, v, w, i);
addEdge(edge2, head2, v, u, w, i);
}
cin >> s >> t >> k;
}
void spfa(int ss)
{
for(int i = 0; i < maxn; ++i)
dis[i] = INF;
dis[ss] = 0;
queue<int> q;
q.push(ss);
vis[ss] = 1;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u]= 0;
for(int i = head2[u]; ~i; i = edge2[i].next)
{
Edge v = edge2[i];
if(dis[v.to] > dis[u]+v.w)
{
dis[v.to] = dis[u]+v.w;
if(!vis[v.to])
{
q.push(v.to);
vis[v.to] = 1;;
}
}
}
}
}
int AStar()
{
if(dis[s] == INF)
return -1;
if(s == t) //划重点
++k;
priority_queue<Node> q;
q.push(Node(s, 0, dis[s]));
int cnt = 0;
while(!q.empty())
{
Node cur = q.top();
q.pop();
if(cur.x == t)
if(++cnt == k)
return cur.g;
for(int i = head1[cur.x]; ~i; i = edge1[i].next)
{
Edge next = edge1[i];
q.push(Node(next.to, cur.g+next.w, dis[next.to]));
}
}
return -1;
}
void solve()
{
spfa(t);
cout << AStar() << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
init();
read();
solve();
return 0;
}