分层图最短路

分层图最短路,就是在分层图上解决最短路问题
一般模型为:
在一张图上,有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 << ‘\n‘;
 97 //    fclose(stdin);
 98 //    fclose(stdout);
 99     return 0;
100 }

原文地址:https://www.cnblogs.com/ywjblog/p/9270423.html

时间: 2024-11-03 10:01:31

分层图最短路的相关文章

poj3635Full Tank?[分层图最短路]

Full Tank? Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7248   Accepted: 2338 Description After going through the receipts from your car trip through Europe this summer, you realised that the gas prices varied between the cities you v

BZOJ 2763 分层图最短路

突然发现我不会分层图最短路,写一发. 就是同层中用双向边相连,用单向边连下一层 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <string> 5 #include <cstring> 6 #include <queue> 7 #include <vector> 8 #define pa pair<int,int

HDU 5669 Road(线段树建树)(分层图最短路)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5669 [分析]线段树建树+分层图最短路 #include <cstdio> #include <map> #include <algorithm> #include <vector> #include <iostream> #include <set> #include <queue> #include <string&

【网络流24题】 No.14 孤岛营救问题 (分层图最短路)

[题意] 1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛, 营救被敌军俘虏的大兵瑞恩. 瑞恩被关押在一个迷宫里, 迷宫地形复杂, 但幸好麦克得到了迷宫的地形图. 迷宫的外形是一个长方形, 其南北方向被划分为 N 行,东西方向被划分为 M 列,于是整个迷宫被划分为 N× M 个单元.每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示.南北或东西方向相邻的 2 个单元之间可能互通, 也可能有一扇锁着的门,或者是一堵不可逾越的墙.迷宫中有一些单元存放着钥匙, 并

NEFU 1132 分层图最短路

点击打开链接 题意:给出起点和终点,然后有k次机会使得路径上的某些路径的值减半,问从起点到终点的最小花费 思路:很明显的分层图最短路嘛,自己也没有去研究算法的含义,队友和我说就是将图分成了n层,然后将这个可以减半的费用连接这n层,然后跑个类似的最短了就可以了,今天改了一个模版,改成了自己喜欢的风格,明天在做几道分层图在看看概念把,这题就是个模版题 #include <queue> #include <vector> #include <stdio.h> #include

BZOJ_2662_[BeiJing wc2012]冻结_分层图最短路

Description “我要成为魔法少女!”     “那么,以灵魂为代价,你希望得到什么?” “我要将有关魔法和奇迹的一切,封印于卡片之中„„”        在这个愿望被实现以后的世界里,人们享受着魔法卡片(SpellCard,又名符 卡)带来的便捷.   现在,不需要立下契约也可以使用魔法了!你还不来试一试?   比如,我们在魔法百科全书(Encyclopedia  of  Spells)里用“freeze”作为关 键字来查询,会有很多有趣的结果. 例如,我们熟知的Cirno,她的冰冻魔

P4568 飞行路线 分层图最短路

P4568 飞行路线 分层图最短路 分层图最短路 问题模型 求最短路时,可有\(k\)次更改边权(减为0) 思路 在普通求\(Dijkstra\)基础上,\(dis[x][j]\)多开一维\(j\)以存已用了多少次机会,然后每次松弛时,做完普通松弛操作后,还要使用一次机会(如果可以),类同\(DP\). 每次普通松弛: \[ dis[to][j]=min\{dis[cur][j], dis[to][j]\} \] 如果还可以使用(\(j<k\)): \[ dis[to][j+1] = min\{

[P4568][JLOI2011] 飞行路线 (分层图+最短路)

题意:有n个城市,m条航线,每条航线都有一个权值,并且还多了k次免费航行的机会,求1~n的最短路: 做法:分层图+最短路: 1.分层图:因为多了k次免费航行,所以可以考虑建出k+1个图,然后跑一遍最短路: 2.最短路:既然能写分层图,那么最短路应该都会了吧,可以用 dijkstra 或 SPFA : 附上代码: #include<cstdio> #include<cstring> #include<iostream> #include<algorithm>

【题解】P3645 [APIO2015]雅加达的摩天楼(分层图最短路)

[题解]P3645 [APIO2015]雅加达的摩天楼(分层图最短路) 感觉分层图是个很灵活的东西 直接连边的话,边数是$O(n^2)$的过不去 然而我们有一个优化的办法,可以建一个新图$G=(V,E)$其中$V$和原图$V$一一对应且连接一个$0$边,此外每个点向V中的$i+-d$连边. 类似网络流的办法瞎建就行了. 过不了uoj //@winlere #include<iostream> #include<cstdio> #include<cstring> #inc