【题型】分层图最短路
本蒟蒻刚刚开通博客,这是我第一篇文文,写得不好请大家见谅
学最短路已经有一段时间了,也在洛谷上刷了挺多的题,机房第一次考试,本蒟蒻 满(垂)怀(头)信(丧)心(气)地接到考题准备AK爆零全场,却死在了T2上
T2:洛谷P1948
这个题学长们改了改数据范围,让k无限大(对于我来说),使这个题只能用二分答案来做,但我还是 义(no)无(zuo)反(no)顾(die)地要用分层图做,可惜,代码没能实现(完美爆零)
关于分层图最短路
大概思路:将同一个图复制成多层,从每个图的每个点向下一层连一条边(至于连下一层的哪个点下面再说)
大约是这样的:
(图画地不好请见谅)
可以理解成一栋楼,有很多楼梯,我们可以从一层通过楼梯走到下一层
为什么要建分层图
通过洛谷P4568可以理解
实现道路免费(或道路优惠),而且道路的免费(或优惠)还是不定项的
假如有一条边,连接u点和v点,边权为w
可以从上一层图中的编号为u的点向下一层图的编号为v的点连一条边权为0的边(代表免费)
同时在上一层图中的编号为u的点向本层图的编号为v的点连一条边权为w的边(代表不免费)
这样当你走到u点时,就有两个选择:免费或不免费
而建k层图,就代表着有k次免费的优惠
注意:要把每层图(除了最后一层)的终点向下一层图连一条边权为0的边(防止最佳路径不全部用完k次优惠)
伪代码实现(链式前向星):
scanf("%d %d %d", &n, &p, &k);//输入点的个数n,边的个数p,层数k for (int i = 1; i <= p; i++){ scanf("%d %d %d", &x, &y, &z);//输入x点与y点相连,边权为z add(x, y, z); //add(y, x, z);无向图需添加这一句 for (int j = 1; j <= k; j++){ add(x + (j - 1) * n, y + j * n, 0);//使这个点向下层图连条边 //add(y + (j - 1) * n, x + j * n, 0); add(x + j * n, y + j * n, z);//在下层图中构建与上层图相同的边 //add(y + j * n, x + j * n, z); } }
我是用链式前向星做的,也可以用四维数组来存(如果你空间够的话)
伪代码实现(四维数组):
scanf("%d %d %d", &n, &p, &k);//输入点的个数n,边的个数p,层数k for (int i = 1; i <= p; i++){ scanf("%d %d %d", &x, &y, &z);//输入x点与y点相连,边权为z a[x][1][y][1] = z;//x后的数代表第几层的x,y后的数代表第几层的y //a[y][1][x][1] = z;无向图需添加这一句 for (int j = 1; j <= k; j++){ a[x][j][y][j+1] = 0;//使这个点向下层图连条边 //a[y][j+1][x][j] = 0; a[x][j][y][j] = z;//在下层图中构建与上层图相同的边 //a[y][j][x][j] = z; } }
当然肯定还有一些用数组来存但不至于用四维的方式,但我还是个蒟蒻,不会......
还是由dalao们来想办法吧~~
洛谷P4568代码实现:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <queue> #define LL long long #define MAXN 1000001 #define M(i, j) make_pair(i, j) using namespace std; int head[MAXN]; int n, p, k, js=0, cnt=0, sum=0; LL dis[MAXN]; bool vis[MAXN]; priority_queue< pair<int,int>, vector< pair<int,int> >, greater< pair<int,int> > > q; struct edge{ int v, nxt, b; long long w; }e[5000001 << 1]; void add(int u, int v, int w){ e[++js].v = v; e[js].w = w; e[js].nxt = head[u]; head[u] = js; } void dijkstra(int s){//dij堆优化板子 int l, k, v, w; memset(vis, 0, sizeof(vis)); memset(dis, 63, sizeof(dis)); dis[s] = 0; q.push(M(0, s)); while (!q.empty()){ l = q.top().first; k = q.top().second; q.pop(); if (vis[k]) continue; vis[k] = 1; for (int i = head[k]; i; i = e[i].nxt){ v = e[i].v; w = e[i].w; if (dis[v] > l + w){ dis[v] = l + w; if (!vis[v]) q.push(M(dis[v], v)); } } } } int main(){ int x, y, z, s, t; scanf("%d %d %d", &n, &p, &k); for (int i = 1; i <= p; i++){ scanf("%d %d %d", &x, &y, &z); add(x, y, z); add(y, x, z); for (int j = 1; j <= k; j++){ add(x + (j - 1) * n, y + j * n, 0); add(y + (j - 1) * n, x + j * n, 0); add(x + j * n, y + j * n, z); add(y + j * n, x + j * n, z); } } for (int i = 1; i <= k; i++) add(t + (i - 1) * n, t + i * n, 0);//这个是上文的注意(看到有的dalao提醒过) dijkstra(s); printf("%d\n", dis[t + k * n]); return 0; }
提供分层图最短路的其他题目(洛谷里的):