题目链接Remmarguts’ Date
由A*算法可以直到,当目标点第一出优先队列时,一定是最优解,所以当目标点T第k次出队时,就是第k短路,这是可以用反证法证明的:
假设这是第m次出队(m > 1) ,那么这一次出队是当前所有解中最优(A*性质),所以假设此次路径短于第m - 1次出队长度,显然上一次出队的值应该就是此次出队的值,与假设不符,故该路为第m短路
h(x)就可以采用终点到各个点的最短路作为启发式函数,因为可以保证在找第k短路的过程中某点到终点的h(x) <= g(x)
下面是poj2449的代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <deque>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef pair <int,int> PII;
typedef pair <int, pair <int, int> > PIII;
const int inf = 0x7fffffff;
const int maxn= 1e3 + 5;
const int maxm = 2e5 + 5;
const int mod = 1e9 + 7;
const int dir8[8][2] = {
{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
const int dir4[4][2] = {
{-1,0},{1,0},{0,-1},{0,1}};
int n, m;
int h[maxn], rh[maxn], e[maxm], w[maxm], ne[maxm], idx;
int dist[maxn], st, ed, k;
int vis[maxn];
void add(int h[], int a, int b, int c)
{
idx++;
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx;
}
void dij()
{
memset(dist, 0x3f, sizeof dist);
priority_queue <PII, vector <PII>, greater <PII> > heap;
dist[ed] = 0;
heap.push({0, ed});
while(heap.size()) {
PII t = heap.top();
heap.pop();
if(vis[t.second]) continue;
vis[t.second] = 1;
for(int i = rh[t.second]; i; i = ne[i]) {
int j = e[i];
if(dist[j] > dist[t.second] + w[i]) {
dist[j] = dist[t.second] + w[i];
heap.push({dist[j], j});
}
}
}
}
int astar()
{
memset(vis, 0, sizeof vis);
priority_queue <PIII, vector <PIII>, greater <PIII> > heap;
heap.push({dist[st], {0, st}});
while(heap.size()) {
PIII t = heap.top();
heap.pop();
int ver = t.second.second, distance = t.second.first;
vis[ver]++;
if(vis[ed] == k) return distance;
for(int i = h[ver]; i; i = ne[i]) {
int j = e[i];
heap.push({distance + dist[j] + w[i], {distance + w[i], j}});
}
}
return -1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) {
int u, v, l;
scanf("%d%d%d", &u, &v, &l);
//建立反图
add(h, u, v, l), add(rh, v, u, l);
}
scanf("%d%d%d", &st, &ed, &k);
dij();
//由于当s == t时 最短路为0,所以要求第k + 1短路
if(st == ed) k++;
int ans = astar();
printf("%d\n", ans);
return 0;
}
题目2 第k短路
因为A并不能保证除目标点之外的所有值第k次出队时都是起点到该点的第k短路(除非满足h(x)的一致性),所以上面的代码是没有进行剪枝的
但是这个题,会发现,题目和poj2449是一样的,于是选择之间提交代码,结果发现T了,根据提供的TLE的数据发现这组数据是无解的情况,在当出现无解情况时,A算法的效率会极其低下,这时候就要对其进行剪枝,这里我们采取的策略是当某个点出队>k次时,就达到了起点到该点的第k短路,就不在入队了,从而减轻了计算时间,但是怎么能保证每一次出队时这些点都是距离最短呢?这就提到了上文中当h(x)满足一致性时,由于对启发式函数的一致性证明比较困难,在竞赛中也不容易去证明,所以在竞赛中遇到卡判重或者卡剪枝的情况,之间默认其启发式函数具有一致性即可,不需太过在意证明,当然这只是我的经验之谈… 还是希望可以完备的判断其是否具有一致性的,只是奈何能力属实不太够,也并不打算专攻图论部分…
剪枝代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <deque>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef pair <int,int> PII;
typedef pair <int, pair <int, int> > PIII;
const int inf = 0x7fffffff;
const int maxn= 1e3 + 5;
const int maxm = 2e5 + 5;
const int mod = 1e9 + 7;
const int dir8[8][2] = {
{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
const int dir4[4][2] = {
{-1,0},{1,0},{0,-1},{0,1}};
int n, m;
int h[maxn], rh[maxn], e[maxm], w[maxm], ne[maxm], idx;
int dist[maxn], st, ed, k;
int vis[maxn];
void add(int h[], int a, int b, int c)
{
idx++;
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx;
}
void dij()
{
memset(dist, 0x3f, sizeof dist);
priority_queue <PII, vector <PII>, greater <PII> > heap;
dist[ed] = 0;
heap.push({0, ed});
while(heap.size()) {
auto t = heap.top();
heap.pop();
if(vis[t.second]) continue;
vis[t.second] = 1;
for(int i = rh[t.second]; i; i = ne[i]) {
int j = e[i];
if(dist[j] > dist[t.second] + w[i]) {
dist[j] = dist[t.second] + w[i];
heap.push({dist[j], j});
}
}
}
}
int astar()
{
memset(vis, 0, sizeof vis);
priority_queue <PIII, vector <PIII>, greater <PIII> > heap;
heap.push({dist[st], {0, st}});
while(heap.size()) {
auto t = heap.top();
heap.pop();
int ver = t.second.second, distance = t.second.first;
vis[ver]++;
if(vis[ed] == k) return distance;
for(int i = h[ver]; i; i = ne[i]) {
int j = e[i];
if(vis[ver] > k) continue;
heap.push({distance + dist[j] + w[i], {distance + w[i], j}});
}
}
return -1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) {
int u, v, l;
scanf("%d%d%d", &u, &v, &l);
//建立反图
add(h, u, v, l), add(rh, v, u, l);
}
scanf("%d%d%d", &st, &ed, &k);
dij();
//由于当s == t时 最短路为0,所以要求第k + 1短路
if(st == ed) k++;
int ans = astar();
printf("%d\n", ans);
return 0;
}