k 短路
问题描述¶
给定一个有
A*算法¶
A*算法定义了一个对当前状态
在求解
由于设计的距离函数和估价函数,对于每个状态需要记录两个值,为当前到达的结点
开始我们将初始状态
优化:由于只需要求出从初始结点到目标结点的第
当图的形态是一个
参考实现¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 5010;
const int maxm = 400010;
const int inf = 2e9;
int n, m, s, t, k, u, v, ww, H[maxn], cnt[maxn];
int cur, h[maxn], nxt[maxm], p[maxm], w[maxm];
int cur1, h1[maxn], nxt1[maxm], p1[maxm], w1[maxm];
bool tf[maxn];
void add_edge(int x, int y, double z) {
cur++;
nxt[cur] = h[x];
h[x] = cur;
p[cur] = y;
w[cur] = z;
}
void add_edge1(int x, int y, double z) {
cur1++;
nxt1[cur1] = h1[x];
h1[x] = cur1;
p1[cur1] = y;
w1[cur1] = z;
}
struct node {
int x, v;
bool operator<(node a) const { return v + H[x] > a.v + H[a.x]; }
};
priority_queue<node> q;
struct node2 {
int x, v;
bool operator<(node2 a) const { return v > a.v; }
} x;
priority_queue<node2> Q;
int main() {
scanf("%d%d%d%d%d", &n, &m, &s, &t, &k);
while (m--) {
scanf("%d%d%d", &u, &v, &ww);
add_edge(u, v, ww);
add_edge1(v, u, ww);
}
for (int i = 1; i <= n; i++) H[i] = inf;
Q.push({t, 0});
while (!Q.empty()) {
x = Q.top();
Q.pop();
if (tf[x.x]) continue;
tf[x.x] = true;
H[x.x] = x.v;
for (int j = h1[x.x]; j; j = nxt1[j]) Q.push({p1[j], x.v + w1[j]});
}
q.push({s, 0});
while (!q.empty()) {
node x = q.top();
q.pop();
cnt[x.x]++;
if (x.x == t && cnt[x.x] == k) {
printf("%d\n", x.v);
return 0;
}
if (cnt[x.x] > k) continue;
for (int j = h[x.x]; j; j = nxt[j]) q.push({p[j], x.v + w[j]});
}
printf("-1\n");
return 0;
}
|
可持久化可并堆优化 k 短路算法¶
最短路树与任意路径的关系与性质¶
在反向图上从
所谓最短路径树,就是满足从树上的每个结点
设一条从
-
对于一条不在
T e u v w \Delta e=dist_v+w-dist_u P L_P=dist_s+\sum_{e\in P'} \Delta e -
将
P P' s t P' e_1,e_2 u_{e_2} v_{e_1} T P e_1,e_2 -
对于一个确定存在的
P' S S'=P' 2 P' T
问题转化¶
性质
性质
性质
那么问题转化为:求
算法描述¶
由于性质
我们用一个小根堆来维护这样的边集
初始我们将起点为
每次取出堆顶的一个边集
-
替换
S \Delta e -
在最后一条边后接上一条边,设
x S 2 x x T
将生成的新边集也加入小根堆。重复以上操作
对于每个结点
-
替换
S t -
在最后一条边后接上一条新的边,设
x S x
用这种方法,每次生成新的边集只会扩展出最多三个结点,小根堆中的结点总数是
所以此算法的瓶颈在合并一个结点与其在
可持久化可并堆优化¶
在阅读本内容前,请先了解 可持久化可并堆 的相关知识。
使用可持久化可并堆优化合并一个结点与其在
参考实现¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 200010;
int n, m, s, t, k, x, y, ww, cnt, fa[maxn];
struct Edge {
int cur, h[maxn], nxt[maxn], p[maxn], w[maxn];
void add_edge(int x, int y, int z) {
cur++;
nxt[cur] = h[x];
h[x] = cur;
p[cur] = y;
w[cur] = z;
}
} e1, e2;
int dist[maxn];
bool tf[maxn], vis[maxn], ontree[maxn];
struct node {
int x, v;
node* operator=(node a) {
x = a.x;
v = a.v;
return this;
}
bool operator<(node a) const { return v > a.v; }
} a;
priority_queue<node> Q;
void dfs(int x) {
vis[x] = true;
for (int j = e2.h[x]; j; j = e2.nxt[j])
if (!vis[e2.p[j]])
if (dist[e2.p[j]] == dist[x] + e2.w[j])
fa[e2.p[j]] = x, ontree[j] = true, dfs(e2.p[j]);
}
struct LeftistTree {
int cnt, rt[maxn], lc[maxn * 20], rc[maxn * 20], dist[maxn * 20];
node v[maxn * 20];
LeftistTree() { dist[0] = -1; }
int newnode(node w) {
cnt++;
v[cnt] = w;
return cnt;
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (v[x] < v[y]) swap(x, y);
int p = ++cnt;
lc[p] = lc[x];
v[p] = v[x];
rc[p] = merge(rc[x], y);
if (dist[lc[p]] < dist[rc[p]]) swap(lc[p], rc[p]);
dist[p] = dist[rc[p]] + 1;
return p;
}
} st;
void dfs2(int x) {
vis[x] = true;
if (fa[x]) st.rt[x] = st.merge(st.rt[x], st.rt[fa[x]]);
for (int j = e2.h[x]; j; j = e2.nxt[j])
if (fa[e2.p[j]] == x && !vis[e2.p[j]]) dfs2(e2.p[j]);
}
int main() {
scanf("%d%d%d%d%d", &n, &m, &s, &t, &k);
for (int i = 1; i <= m; i++)
scanf("%d%d%d", &x, &y, &ww), e1.add_edge(x, y, ww), e2.add_edge(y, x, ww);
Q.push({t, 0});
while (!Q.empty()) {
a = Q.top();
Q.pop();
if (tf[a.x]) continue;
tf[a.x] = true;
dist[a.x] = a.v;
for (int j = e2.h[a.x]; j; j = e2.nxt[j]) Q.push({e2.p[j], a.v + e2.w[j]});
}
if (k == 1) {
if (tf[s])
printf("%d\n", dist[s]);
else
printf("-1\n");
return 0;
}
dfs(t);
for (int i = 1; i <= n; i++)
if (tf[i])
for (int j = e1.h[i]; j; j = e1.nxt[j])
if (!ontree[j])
if (tf[e1.p[j]])
st.rt[i] = st.merge(
st.rt[i],
st.newnode({e1.p[j], dist[e1.p[j]] + e1.w[j] - dist[i]}));
for (int i = 1; i <= n; i++) vis[i] = false;
dfs2(t);
if (st.rt[s]) Q.push({st.rt[s], dist[s] + st.v[st.rt[s]].v});
while (!Q.empty()) {
a = Q.top();
Q.pop();
cnt++;
if (cnt == k - 1) {
printf("%d\n", a.v);
return 0;
}
if (st.lc[a.x])
Q.push({st.lc[a.x], a.v - st.v[a.x].v + st.v[st.lc[a.x]].v});
if (st.rc[a.x])
Q.push({st.rc[a.x], a.v - st.v[a.x].v + st.v[st.rc[a.x]].v});
x = st.rt[st.v[a.x].x];
if (x) Q.push({x, a.v + st.v[x].v});
}
printf("-1\n");
return 0;
}
|
习题¶
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用