题目链接
http://poj.org/problem?id=2449
题意
求一个有向图的给定起点和终点的K短路
思路
A* + SPFA
A*算法的核心是估价函数f(n) = g(n) + h(n)的设计。
对于该题,设g(n)为当前点到起点的最近距离,h(n)为当前点到终点的最近距离,f(n) = h(n) + g(n)的第k大即为起点到终点的第K短路。
该题为有向图,于是先将所有边反向,从终点做一次单源最短路径,求出h(n)。在从起点单源最短路径,原来的d[]数组实际上就是估价函数f()。当终点第K次从优先队列中出来的时候得到的就是第K短路
细节
- 该图为有向图,因此求每个点到终点的最短路时,从终点做单源最短路径时应将所有边反向
- 起点和终点相同的时候,因为该题默认必须要经过边,所以要K++
证明
在图上,只要h(n)满足一致性,那么A*算法得到的一定是最优解
定义:
f(n):从初始状态到目标状态的估计代价
g(n):从初始状态到目前状态的实际代价
h(n):从目前状态到目标状态的估计代价
h(n)的一致性:设v为u的后继节点,则h(u) ≤ h(v) + w(u, v) (w(u, v)为u到v的代价)
该题的证明
首先证明该题中h(n)满足一致性:
在该题中,h(n)定义为当前节点u到目标节点t的最短路径,v为u的后继结点,那么只需要证明:h(u) ≤ h(v) + w(u, v)
反证法,假设h(u) > h(v) + w(u, v)。那么求t到u的最短路径时,h(u)可以直接更新为h(v) + w(u, v),即h(u)不是t到u的最短路径,矛盾。因此h(n)满足一致性定理
其次再证明在图搜索上,只要h(n)满足一致性,A*算法产生的即为最优解
I.证明沿着路径上扩展的f(n)一定是非递减的:
设v为u的后继结点,则f(v) = g(v) + h(v) = g(u) + w(u, v) + h(v) ≥ g(u) + h(u) = f(n)
II.证明第一次选择扩展目标节点的时候得到的是s到t的最优解
反证法, 假设存在另一个结点t’,并且t’更优,那么选择扩展的时候,一定先扩展t’而不是t,因此第一次扩展目标节点t的时候得到的是t的最优值
那么我们就得到了第一次扩展t的时候得到的是s到t的最短路,那么同理,第k次扩展到目标节点的时候得到的是第k短路,得证
代码
#include<iostream>
#include<cstring>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#include<sstream>
#include<iomanip>
#include<fstream>
#include<cstdio>
#include<cstdlib>
#include<climits>
#include<deque>
using namespace std;
#define INF 10000000
#define PI acos(-1.0)
#define LL long long
#define PII pair<int, int>
#define PLL pair<LL, LL>
#define mp make_pair
#define IN freopen("in.txt", "r", stdin)
#define OUT freopen("out.txt", "wb", stdout)
#define scan(x) scanf("%d", &x)
#define scan2(x, y) scanf("%d%d", &x, &y)
#define sqr(x) (x) * (x)
const int maxn = 1000 + 5;
int N, M;
struct Edge {
int from, to, dist;
Edge(int u, int v, int w) : from(u), to(v), dist(w) {
}
};
vector<Edge> edges;
vector<Edge> redges;
vector<int> G[maxn];
vector<int> RG[maxn];
void addedge(int u, int v, int w) {
edges.push_back(Edge(u, v, w));
int s = edges.size() - 1;
G[u].push_back(s);
redges.push_back(Edge(v, u, w));
s = redges.size() - 1;
RG[v].push_back(s);
}
int h[maxn];
void spfa(int s) {
int inq[maxn];
memset(inq, 0, sizeof(inq));
queue<int> q;
for (int i = 1; i <= N; i++) h[i] = INF;
h[s] = 0;
inq[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = 0;
int s = RG[u].size();
for (int i = 0; i < s; i++) {
Edge &e = redges[RG[u][i]];
if (h[u] < INF && h[e.to] > h[u] + e.dist) {
h[e.to] = h[u] + e.dist;
if (!inq[e.to]) {
inq[e.to] = 1;
q.push(e.to);
}
}
}
}
}
struct node {
int _f, _g, _v;
node (int x, int y, int z) : _f(x), _g(y), _v(z) {
}
};
struct cmp {
bool operator() (node x, node y) {
return x._f > y._f;
}
};
int Astar(int S, int T, int K) {
if (h[T] == INF) return -1;
if (S == T) K++;
int cnt[maxn], shortest;
memset(cnt, 0, sizeof(cnt));
priority_queue<node, vector<node>, cmp> q;
q.push(node(h[S] , 0, S));
while (!q.empty()) {
node t = q.top();
q.pop();
int u = t._v;
cnt[u]++;
if (u == T && cnt[T] == K) return t._f;
int s = G[u].size();
for (int i = 0; i < s; i++) {
Edge &e = edges[G[u][i]];
int g = t._g + e.dist;
int f = h[e.to] + g;
q.push(node(f, g, e.to));
}
}
return -1;
}
int main() {
scan2(N, M);
for (int i = 0; i < M; i++) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
addedge(x, y, z);
}
int S, T, K;
scanf("%d%d%d", &S, &T, &K);
spfa(T);
printf("%d\n", Astar(S, T, K));
return 0;
}