分层图可以来解决在图上的决策最短路问题。
按P4568 [JLOI2011]飞行路线这个题来说。
走每条边时可以有
K
K
K次让这条边免费的机会。
建图的方法是,原图先建好,针对
K
K
K次免费的机会,每次机会新建一层图,新建的每层图与原图相同,这里层就理解为上下排列着的,然后对于原图中的
u
→
v
u\rightarrow v
u→v的边,在每相邻两层图之间连一条
u
→
v
u\rightarrow v
u→v的边,层间的边边权为
0
0
0,例如下图
K
=
1
K=1
K=1,为了简洁画的有向图。
蓝色边就是层间的边,例如原图中有
1
→
2
1\rightarrow 2
1→2的边,在分层图中就从原图中的
1
1
1向下一层的
2
2
2建边,边权为
0
0
0。
这样跑最短路时每垮一层图就表示用掉了一次免费的机会。
注意针对不同的题目最优策略不一定在第
K
K
K层图的
n
n
n号点,每层图都可能有最优策略,所以为了保险可以对每层图的
n
n
n点的值取一个最优。
这样连边会多连出很多边,所以注意计算加边数组的大小,而且每层图会多开点,还要注意计算点集大小。
洛谷P4568 [JLOI2011]飞行路线
这个题边集是
50000
50000
50000的,
K
K
K最多是
10
10
10,原图加两条边,其它层的图及层之间会加
4
4
4条边,每次最多加
42
42
42条边,所以边集要开到
(
4
∗
10
+
2
)
∗
50000
=
2100000
(4*10+2)*50000=2100000
(4∗10+2)∗50000=2100000
每层图要多开
n
n
n个点,所以点集是
10000
+
10000
∗
10
=
110000
10000+10000*10=110000
10000+10000∗10=110000的,注意都
+
10
+10
+10
下面的
s
p
f
a
spfa
spfa用了
s
l
f
slf
slf优化
#include <bits/stdc++.h>
#define A 110010
#define B 2100010
using namespace std;
struct node {int next, to, w;}e[B];
int head[A], num;
void add(int fr, int to, int w) {
e[++num].next = head[fr]; e[num].to = to;
e[num].w = w; head[fr] = num;
}
int n, m, k, s, t, a, b, c, dis[A]; bool vis[A];
void spfa(int s) {
memset(dis, 0x3f, sizeof dis); memset(vis, 0, sizeof vis);
deque<int> q; dis[s] = 0; q.push_back(s);
while (!q.empty()) {
int fr = q.front(); q.pop_front(); vis[fr] = 0;
for (int i = head[fr]; i; i = e[i].next) {
int ca = e[i].to;
if (dis[ca] > dis[fr] + e[i].w) {
dis[ca] = dis[fr] + e[i].w;
if (!vis[ca]) {
vis[ca] = 1;
if (q.empty() or dis[ca] > dis[q.front()]) q.push_back(ca);
else q.push_front(ca);
}
}
}
}
}
int main(int argc, char const *argv[]) {
cin >> n >> m >> k >> s >> t;
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &a, &b, &c);
add(a, b, c); add(b, a, c);
for (int j = 1; j <= k; j++) {
add(a + (j - 1) * n, b + j * n, 0);
add(b + (j - 1) * n, a + j * n, 0);
add(a + j * n, b + j * n, c);
add(b + j * n, a + j * n, c);
}
}
spfa(s);
for (int i = 0; i <= k; i++) ans = min(ans, dis[t + n * i]);
cout << ans << endl;
}