分层图可以来解决在图上的决策最短路问题。
按P4568 [JLOI2011]飞行路线这个题来说。
走每条边时可以有次让这条边免费的机会。
建图的方法是,原图先建好,针对次免费的机会,每次机会新建一层图,新建的每层图与原图相同,这里层就理解为上下排列着的,然后对于原图中的
的边,在每相邻两层图之间连一条
的边,层间的边边权为
,例如下图
,为了简洁画的有向图。
蓝色边就是层间的边,例如原图中有的边,在分层图中就从原图中的
向下一层的
建边,边权为
。
这样跑最短路时每垮一层图就表示用掉了一次免费的机会。
注意针对不同的题目最优策略不一定在第层图的
号点,每层图都可能有最优策略,所以为了保险可以对每层图的
点的值取一个最优。
这样连边会多连出很多边,所以注意计算加边数组的大小,而且每层图会多开点,还要注意计算点集大小。
洛谷P4568 [JLOI2011]飞行路线 这个题边集是的,
最多是
,原图加两条边,其它层的图及层之间会加
条边,每次最多加
条边,所以边集要开到
每层图要多开个点,所以点集是
的,注意都
下面的用了
优化
#include <bits/stdc++.h>
#define
#define
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;
}