分层图最短路
Posted ywjblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分层图最短路相关的知识,希望对你有一定的参考价值。
分层图最短路,就是在分层图上解决最短路问题
一般模型为:
在一张图上,有k次机会可以通过一条边而不需要计算权值(免费过路),求从起点到终点的最短路线
常规思路:
想象将一个点拆分为k + 1个点,分别表示到这个点时,免费权消耗了0次,1次,2次......k次
这样实际我们可以把这k个点想象成对应dp的不同的状态
dis[i][j]表示到第i个点时,消耗了j次乘车权后的最短路线
我们用to表示要到达的点,x表示父亲节点,就有
dis[to][j] = min(dis[x][j] + val(x, to), dis[x][j - 1])
因为我们跑最短路时是从前向后跑,也就是当前状态推出后继状态,所以实际上我们可以推出两个可能状态
如果我们消耗了免费过路权
dis[to][j] = min{dis[x][j - 1]}
如果我们没消耗免费过路权
dis[to][j] = min{dis[x][j] + val(x, to)}
这就提醒我们,我们的队列在加入到达某个点的同时,要分别记录到达这个点时的两种不同的状态,以免造成情况遗漏
也就是q[i][j]表示到第i个点时,第i个点在j的情况下我们消耗了几次免费过路权,j为0或是1,0表示没有消耗免费过路权,1表示消耗了免费过路权
到这里我们就能与上面的拆点联系上了,我们想,到了终点时,可能有:用了0次免费过路权,用了1次免费过路权,用了2次,用了3次....用了k次
也就是k+1种可能状态,此时我们把这k+1种状态,每种状态都想象成原本的这个点拆分出来的一个点,也就相当于这个点拆分出了k+1个点,就和上面接上了
然后我们合理外推,对于每一个点都可能出现这样的情况,也就相当于每一个点都拆分成了k+1个点,这n*(k+1)个点之间彼此连接,跑最短路,这样可能有点抽象
实际上把这想象成一个dp的过程是最好理解的
例题1:move(集训考试题)
题目描述
给定一张地图一共有 n 个城市,城市编号为 0 ~ n - 1,这 n 个城市通过 m
条铁路连接(走一条铁路视为乘车一次)。而小A 想从城市 s 出发,到 达城市 t
结束。小A 可以免费乘车 k 次,现在他想知道,他这 次旅游的最少花费是多少?
输入格式
第一包含三个整数 n, m, k,含义见题目描述。
第二有两个整数 s, t,表示小A 的出发城市和结束城市。
接下来 m 行,每行三个整数 x, y, z,表?在城市 x 和 y 之间有一条铁路相
连,乘车花费为 z。
输出格式
输出一行,一个整数表示答案。
样例输入
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
样例输出
8
数据范围
对于 30% 的数据, 2 ≤ n ≤ 50, 1 ≤ m ≤ 300, k = 0;
对于 50% 的数据, 2 ≤ n ≤ 600, 1 ≤ m ≤ 6000, 0 ≤ k ≤ 1;
很标准的模板型题目。
思路就是上面的思路,不加赘述,代码实现口胡不好说,直接读代码吧=-=
1 #include<iostream> 2 #include<iomanip> 3 #include<cstdio> 4 #include<cstring> 5 #include<ctime> 6 #include<cmath> 7 #include<algorithm> 8 #include<cstdlib> 9 using namespace std; 10 const int maxn = 100086; 11 const int inf = 1000000007; 12 struct node { 13 int y, net, v; 14 }e[maxn]; 15 int dis[maxn][11]; 16 int n, m, k, st, ed; 17 int lin[maxn], len = 0; 18 int q[maxn][2];//第二维, 0表示当前点没有拆点,1表示当前点进行了拆点,拆成了k+1个点q[i][1]存储的是第i个点拆出的点的编号 19 bool vis[maxn][11]; 20 21 inline int read() { 22 int x = 0, y = 1; 23 char ch = getchar(); 24 while(!isdigit(ch)) { 25 if(ch == ‘-‘) y = -1; 26 ch = getchar(); 27 } 28 while(isdigit(ch)) { 29 x = (x << 1) + (x << 3) + ch - ‘0‘; 30 ch = getchar(); 31 } 32 return x * y; 33 } 34 35 inline void insert(int xx, int yy, int vv) { 36 e[++len].net = lin[xx]; 37 e[len].v = vv; 38 e[len].y = yy; 39 lin[xx] = len; 40 } 41 42 inline void spfa(int st) { 43 int head = 0, tail = 1; 44 for(int i = 0; i < n; ++i) 45 for(int j = 0; j <= k; ++j) 46 dis[i][j] = inf; 47 int x, j;//j实际表示使用了多少次免费乘车权,//我们将一个点拆了多次,j同时作为这些被拆出的点的编号 48 memset(vis, 0, sizeof(vis)); 49 vis[st][0] = 1, dis[st][0] = 0; 50 q[1][0] = st, q[1][1] = 0; 51 while(head != tail) { 52 head = (head + 1) % 100003; 53 x = q[head][0]; 54 j = q[head][1]; 55 //vis[x][j] = 1; 56 for(int i = lin[x]; i; i = e[i].net) { 57 int to = e[i].y; 58 if(dis[x][j] + e[i].v < dis[to][j]) {//如果x到to没有使用免费乘车权 59 dis[to][j] = dis[x][j] + e[i].v; 60 if(!vis[to][j]) { 61 vis[to][j] = 1; 62 tail = (tail + 1) % 100003; 63 q[tail][0] = to; 64 q[tail][1] = j; 65 } 66 } 67 if(j < k && dis[x][j] < dis[to][j + 1]) {//如果x到to使用了免费乘车权 68 dis[to][j + 1] = dis[x][j]; 69 if(!vis[to][j + 1]) { 70 vis[to][j + 1] = 1; 71 tail = (tail + 1) % 100003; 72 q[tail][0] = to; 73 q[tail][1] = j + 1; 74 } 75 } 76 } 77 vis[x][j] = 0; 78 } 79 } 80 81 int main() { 82 // freopen("move.in", "r", stdin); 83 // freopen("move.out", "w", stdout); 84 n = read(), m = read(), k = read(); 85 st = read(), ed = read(); 86 for(int i = 1; i <= m; ++i) { 87 int x, y, v; 88 x = read(), y = read(), v = read(); 89 insert(x, y, v); 90 insert(y, x, v); 91 } 92 spfa(st); 93 int ans = inf; 94 for(int i = 0; i <= k; ++i) 95 ans = min(ans, dis[ed][i]); 96 cout << ans << ‘ ‘; 97 // fclose(stdin); 98 // fclose(stdout); 99 return 0; 100 }
以上是关于分层图最短路的主要内容,如果未能解决你的问题,请参考以下文章