分层图,顾名思义,是许多张图一层一层堆叠在同一维度内。如果把一张普通的图定位一层楼,那么分层图就是由许多层楼叠起来的一栋楼。而连接每一层图的边,就相当于一栋楼里的楼梯。
那么,分层图一般用来干什么呢??
当我们遇到这类题:
给你n个点,m条边,每条边都有边权。现在你可以任意选择k条边,使它的边权为0。问从起点到终点的最短路。
在这时,我们就可以通过构建分层图来求解。具体见下文。
分层图的实现主要就难在建图,剩余的就和普通的图没什么区别了。
个人认为,分层图建图的思想类似于扩展域并查集,即:假设一共要建 k k k层图,那么我们就开一个 k × n k \times n k×n的数组,每 n n n个位置存一层图的点,一共分 k k k层。具体见下图:
此时,第 i i i层的点的下标为 1 + ( i − 1 ) × n ∼ n + ( i − 1 ) × n 1 + (i - 1) \times n \sim n + (i - 1) \times n 1+(i−1)×n∼n+(i−1)×n。具体见图见 c o d e code code:
for(int i = 1; i <= m; i ++) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
for(int j = 1; j <= k; j ++) {//除了第一层以外要建k层图
add(x + j * n, y + j * n, z);//第k层图里的边
add(y + j * n, x + j * n, z);
add(x + (j - 1) * n, y + j * n, 0);//连接两层图之间的边
add(y + (j - 1) * n, x + j * n, 0);//注意连接层与层之间的边是单向边
}
}
图建好后,剩下的就是正常的跑最短路了。但是,有一点需要注意:不见得最优答案会产生在第k层图中。也就是,不见得会跑到第k层图中。
什么时候会出现这种情况呢?当 m < k m < k m<k的时候。假设 m = 1 , k = 10 m = 1, k = 10 m=1,k=10,我们只有一条边,也就是说,我们最多建两层图。那么遇到这种情况该怎么办呢?
两种方法任选即可。
具体见例题。
luogu P4568 飞行路线
板子题,套代码即可。
A C c o d e AC code ACcode:
#include
using namespace std;
const int maxn = 1e4 + 5;
const int maxm = 5e4 + 5;
int n, m, k, s, t;
struct my_str {
int to, nxt, val;
}edge[maxm * 50];
int head[maxn * 20], tot = 0;
int dis[maxn * 20];
bool vis[maxn * 20];
struct my_str2 {
int loc, road;
friend bool operator <(my_str2 a, my_str2 b) {
return a.road > b.road;
}
};
priority_queue < my_str2 > qq;
void add(int x, int y, int z) {
edge[++ tot] = (my_str){y, head[x], z};
head[x] = tot;
}
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void Dij() {//堆优化Dij
memset(dis, 0x7f, sizeof dis);
dis[s] = 0;
qq.push((my_str2){s, 0});
while(!qq.empty()) {
int Loc = qq.top().loc;
qq.pop();
if(vis[Loc]) continue;
vis[Loc] = 1;
for(int i = head[Loc]; i != -1; i = edge[i].nxt) {
int To = edge[i].to;
int Val = edge[i].val;
if(dis[To] > dis[Loc] + Val) {
dis[To] = dis[Loc] + Val;
qq.push((my_str2){To, dis[To]});
}
}
}
}
int main() {
n = read(), m = read(), k = read();
s = read(), t = read();
memset(head, -1, sizeof head);
for(int i = 1; i <= m; i ++) {
int x, y, z;
x = read(), y = read(), z = read();
add(x, y, z);
add(y, x, z);
for(int j = 1; j <= k; j ++) {
add(x + j * n, y + j * n, z);
add(y + j * n, x + j * n, z);
add(x + (j - 1) * n, y + j * n, 0);
add(y + (j - 1) * n, x + j * n, 0);
}
}
Dij();
int ans = 0x7f7f7f7f;
for(int i = 0; i <= k; i ++) ans = min(ans, dis[t + i * n]);//统计每一层的答案
printf("%d", ans);
return 0;
}
板子题推荐:
luogu P2939 Revamping Trails G
luogu P4822 冻结