分层图最短路
概念与使用场景
- 分层图:顾名思义就是分很多层的图
- 分层图最短路是指在可以进行分层图的图上解决最短路问题。
- 常用于题目对边的权值提供可选择的操作
- 一般模型:在图上,有k次机会可以直接通过一条边,问起点与终点之间的最短路径。
算法思路
- 将最原始的图作为初始图(第零层),根据所给操作将图分层
- 每进行
k
次操作,那么就将图复制成k
份,第 i 层图代表进行了 i 次操作后的图。
分层图构建思路
- 先将图复制成 k+1 份
0~k+1
- 对于图中的每一条边
<u,v>
从ui
到vi+1
建立与题目所给操作相对应的边(i=0,1,…,k)
- k代表了进行操作的次数,而每层之间点的关系代表了进行了何种操作。
基本模板 O(k*(m+n)log(n))
for(int i = 0; i < m; i ++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w), add(v, u, w);//建原图
//建k次操作后的k层图
for(int j = 1; j <= k; j ++)
{
//建本层,双向
add(u + j * n, v + j * n, w);
add(v + j * n, u + j * n, w);
//连接各层,双向
add(u + (j - 1) * n, v + j * n, 0);
add(v + (j - 1) * n, u + j * n, 0);
}
}
练习题
洛谷P4568–飞行路线
-
分析
- 基础的套用板子题,建好分层图跑最短路即可
- 要注意分析数据范围,分层图要进一步去考虑层数
-
code
#include<bits/stdc++.h> using namespace std; const int N = 2e5 + 10, M = 3e6 + 10; const int inf = 0x3f3f3f3f; typedef pair<int, int>PII; int h[N], e[M], ne[M], w[M], idx; int n, m, t, k, s; int dist[M]; int st[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++; } void dijkstra(int sa) { memset(dist, 0x3f, sizeof dist); dist[sa] = 0; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, sa}); while (heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.second, distance = t.first; if (st[ver]) continue; st[ver] = true; for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[ver] + w[i]) { dist[j] = dist[ver] + w[i]; heap.push({dist[j], j}); } } } } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n >> m; cin >> s >> t; memset(h, -1, sizeof h); for(int i = 0; i < m; i ++) { int u, v, w, last; //通过记录上1个点来完成建边 cin >> u >> v >> w; for(int j = 1; j <= w; j ++) { int x; cin >> x; add(i * n + x, m * n + x, 0); add(n * m + x, i * n + x, u); if(j != 1) { add(i * n + x, i * n + last, v); add(i * n + last, i * n + x, v); } last = x; } } dijkstra(s + n * m); if(dist[n * m + t] == inf) cout << -1 << endl; else cout << dist[n * m + t] << "\n"; return 0; }
洛谷P2939–RTG
-
分析
- 读清题意后可发掘一上一题高度相似
-
code
#include<bits/stdc++.h> using namespace std; typedef pair<int, int> PII; const int N = 2e6 + 10, M = 5e6 + 10; const int inf = 0x3f3f3f3f; int n, m, k, t; int h[N], e[M], ne[M], w[M], idx; int dist[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++; } void dijkstra(int sa) { memset(dist, 0x3f, sizeof dist); dist[sa] = 0; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, sa}); while (heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.second, distance = t.first; if (st[ver]) continue; st[ver] = true; for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[ver] + w[i]) { dist[j] = dist[ver] + w[i]; heap.push({dist[j], j}); } } } } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); memset(h, -1, sizeof h); cin >> n >> m >> k; for(int i = 0; i < m; i ++) { int u, v, w; cin >> u >> v >> w; add(u, v, w), add(v, u, w); for(int j = 1; j <= k; j ++) { add(u + j * n, v + j * n, w); add(v + j * n, u + j * n, w); add(u + (j - 1) * n, v + j * n, 0); add(v + (j - 1) * n, u + j * n, 0); } } dijkstra(1); int ans = inf; for(int i = 0; i <= k; i ++) { ans = min(ans, dist[n + i * n]); } cout << ans << "\n"; return 0; }
牛客–小雨坐地铁
-
分析
- 此题在建图上加了难度,没有所谓覆盖所有点初始状态的初始图
- 我们可以创造一个虚图,来建立操作之间的联系
-
code
#include<bits/stdc++.h> using namespace std; const int N = 2e5 + 10, M = 3e6 + 10; const int inf = 0x3f3f3f3f; typedef pair<int, int>PII; int h[N], e[M], ne[M], w[M], idx; int n, m, t, k, s; int dist[M]; int st[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++; } void dijkstra(int sa) { memset(dist, 0x3f, sizeof dist); dist[sa] = 0; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, sa}); while (heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.second, distance = t.first; if (st[ver]) continue; st[ver] = true; for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[ver] + w[i]) { dist[j] = dist[ver] + w[i]; heap.push({dist[j], j}); } } } } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n >> m; cin >> s >> t; memset(h, -1, sizeof h); for(int i = 0; i < m; i ++) { int u, v, w, last; //通过记录上1个点来完成建边 cin >> u >> v >> w; for(int j = 1; j <= w; j ++) { int x; cin >> x; //向“虚图”创建联系 add(i * n + x, m * n + x, 0); add(n * m + x, i * n + x, u); if(j != 1) { add(i * n + x, i * n + last, v); add(i * n + last, i * n + x, v); } last = x; } } dijkstra(s + n * m); if(dist[n * m + t] == inf) cout << -1 << endl; else cout << dist[n * m + t] << "\n"; return 0; }