「学习笔记」分层图最短路
分层图最短路用于在一个图上可以进行 \(k\) 个决策,每次决策都能使当前的状态改变,我们一般将决策前的状态和决策后的状态连一条边权为决策代价的边。
分层图可以理解为:许多个图平行组成的图,层与层有连边。
对于这种问题,我们通常有两个方法处理:
第一个方法:建 \(k+1\) 的分层图。
第二个方法:多开一维的数组记录。
在最终取答案时,要和每一层的重点进行比较。
我个人比较喜欢第一个方法,所以本文中不会介绍第二种方法。
P4568 [JLOI2011]飞行路线
题目简述:有 \(m\) 条边,\(k\) 个决策可以使当前边的代价为 \(0\),给定起点和终点,求最小代价。
这就是分层图最短路的模板题。
按照上文描述的方法,每一层上下的连边代价都为 \(0\),而每一层中的连边代价都为题目给出的边权。
图建为 \(k+1\) 层时,对于第 \(i\) 层,点 \(u\) 的对应点就是 \(u+i×n\),这里的 \(n\) 为点数。
那么这样我们就能写出建边代码,此题是双向边:
for (int i = 1, u, v, w; i <= m; i ++) {
cin >> u >> v >> w;
for (int j = 0; j <= k; j ++) {
//k次决策,可以不用,从0开始。
add_edge (u + j * n, v + j * n, w);
add_edge (v + j * n, u + j * n, w);
//层内的边
add_edge (u + j * n, v + (j + 1) * n, 0);
add_edge (v + j * n, u + (j + 1) * n, 0);
//两层之间的连接边
}
}
由于此题给出的决策代价为 \(0\),所以两层之间的连接边权为 \(0\)。
而在最后统计答案的时候,我们需要对于每一层的终点取最小值。
因为我们的 \(k\) 次决策可以不全部用完。
于是需要这样写:
for (int i = 0; i <= k; i ++) {
ans = min (ans, dis[i * n + t]);
}
这里的 \(t\) 是终点,加上 \(i×n\) 就是第 \(i\) 层的终点。
正常的跑完最短路完计算答案如上即可。
P1073 [NOIP2009 提高组] 最优贸易
同样是一道经典题。
这道题中的决策就是买入商品卖出商品。
对于每一个商品,我们肯定是需要先买入再卖出。
于是建 \(3\) 层图,第一层是毫无操作的,第二层是买入,第三层是卖出。
于是这道题就做出来了。
这里借鉴一下别人的图方便大家理解:
对于建边,我们先将 \(n\) 个商品进行建图:
for (int i = 1, w; i <= n; i ++) {
scanf ("%d", &w);
add_edge (i, i + n, -w);//对买入层建边
add_edge (i + n, i + 2 * n, w);//对卖出建边
}
然后对于 \(m\) 条边进行建边:
for (int i = 1, x ,y, z; i <= m; i ++) {
scanf ("%d%d%d", &x, &y, &z);
add_edge (x, y, 0);
add_edge (x + n, y + n, 0);
add_edge (x + 2 * n, y + 2 * n, 0);
if (z == 2) {
add_edge (y, x, 0);
add_edge (y + n, x + n, 0);
add_edge (y + 2 * n, x + 2 * n, 0);
}
}
最后对于每一层的终点取 \(max\)。
P4822 [BJWC2012]冻结
依然是 \(k\) 个决策,每次决策的代价就是原边权的 \(\frac{1}{2}\)。
建边如下:
for (int i = 1, x , y, z; i <= m; i ++) {
scanf ("%d%d%d", &x, &y, &z);
for (int j = 0; j <= k; j ++) {
add_edge (j * n + x, j * n + y, z);
add_edge (j * n + y, j * n + x, z);
//层内
add_edge (j * n + x, (j + 1) * n + y, z / 2);
add_edge (j * n + y, (j + 1) * n + x, z / 2);
//两层之间
}
}
P2939 [USACO09FEB]Revamping Trails G
\(k\) 次决策,每次决策的代价是 \(0\),求最小代价。
同样模板题,建边如下:
for (int i = 1, x , y, z; i <= m; i ++) {
scanf ("%d%d%d", &x, &y, &z);
for (int j = 0; j <= k; j ++) {
add_edge (j * n + x, j * n + y, z);
add_edge (j * n + y, j * n + x, z);
add_edge (j * n + x, (j + 1) * n + y, 0);
add_edge (j * n + y, (j + 1) * n + x, 0);
}
}
后言
分层图最短路是一种很好的思想,将决策转化为图论问题进行解决。
相信大家在学习了分层图最短路后,对图论问题的理解会更上一层楼。