分层图学习笔记 & [JLOI2011]飞行路线题解

分层图实际上就是把一个图复制好几份,分别代表每一层图,相邻的层之间的节点如果在原图有边就可以连边,同一层节点之间在原图有边也连边。

这里蓝边就是相邻层的边,黑边就是同一层之间的边。原图就是 ((1,2),(1,3)) 两条边。

Luogu P4568 [JLOI2011]飞行路线

给定一个图,每个图之间有边权,可以将 (k) 个边的边权改为 (0) ,求 (S)(T) 的最短路。

其中 (2leq nleq 10^4,1leq mleq 5 imes 10^4,0leq kleq 10)

可以把这个图分成 (k) 层的分层图,那么现在每个点和每条边代表什么意思呢?定义在第 (i) 层的节点为用了 (i) 次边权取 (0) 的节点,那么每个黑边在同一层图内走代表的就是没有使用边权取 (0), 而走蓝边就是代表把边权取 (0),然后走到下一层图(也就是代表使用了一次边权取 (0) 的操作)。

这个时候以第 (0) 层图的 (s) 为起点跑最短路,答案为每一层 (t) 的最短路的最小值。

(mathcal{Code})

#include<iostream>
#include<cstdio>
#include<queue>
#define pp  std::pair<int, int> 
#define mp  std::make_pair 
#define fir first
#define sec second
const int N = 10010;
const int M = 50010;
const int K = 21;
const int INF = 0x7fffffff;
inline int Min(int x, int y) { return x < y ? x : y; }
inline int read() {
	int r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-') w = 1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		r = (r << 3) + (r << 1) + (ch ^ 48);
		ch = getchar();
	}
	return w ? ~r + 1 : r;
}
int n, m, k, s, t, ans = INF;
int dis[N * K];
bool vis[N * K];
//Edge
int head[N * K], ent;
struct Node {
	int next, to, val;
}e[2 * ((2 + K) * M + K)];
inline void add(int x, int y, int z) {
	e[++ent].to = y; e[ent].val = z; e[ent].next = head[x]; head[x] = ent;
}
std::priority_queue<pp>q;
void Dij() {
	dis[s] = 0;
	q.push(mp(-dis[s], s));
	while(!q.empty()) {
		int x = q.top().sec;
		q.pop();
		if(vis[x]) continue;
		vis[x] = 1;
		for(int i = head[x] ; i; i = e[i].next) {
			int v = e[i].to;
			if(dis[v] > dis[x] + e[i].val) {
				dis[v] = dis[x] + e[i].val;
				if(!vis[v]) q.push(mp(-dis[v], v));
			}
		}
	}
}
signed main() {
	n = read(); m = read(); k = read(); s = read(); t = read();
	for(int i = 1; i <= m; ++i) {
		int x = read(), y = read(), z = read();
		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 = 0; i < n; ++i)
		for(int j = 0; j <= k; ++j)
			dis[i + j * n] = INF;
	Dij();
	for(int i = 0; i <= k; ++i) ans = Min(ans, dis[t + i * n]);
	printf("%d
", ans);
	return 0;
}

练习题目:

Luogu P2939[USACO09FEB]Revamping Trails G

Luogu P4822 [BJWC2012]冻结

Luogu P4822 [BJWC2012]冻结 参考代码

#include<iostream>
#include<cstdio>
#include<queue>
#define pp  std::pair<int, int>
#define mp  std::make_pair
#define frist fsr
#define sec second
const int N = 501000;
int n, m, k;
inline int Min(int x, int y) { return x < y ? x : y; }
inline int read() {
	int r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-') w = 1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		r = (r << 3) + (r << 1) + (ch ^ 48);
		ch = getchar();
	}
	return w ? ~r + 1 : r;
}
int head[N], ent, dis[N];
bool vis[N];
struct Node {
	int next, to, val;
}e[N << 1];
inline void add(int x, int y, int z) {
	e[++ent].to = y; e[ent].next = head[x]; e[ent].val = z; head[x] = ent;
}
std::priority_queue<pp>q;
signed main() {
	n = read(); m = read(); k = read();
	for(int i = 1; i <= m; ++i) {
		int 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, z / 2);
			add(y + (j - 1) * n, x + j * n, z / 2);
		}
	}
	for(int i = 1; i <= n; ++i)
		for(int j = 0; j <= k; ++j)
			dis[i + j * n] = 0x7fffffff;
	dis[1] = 0;
	q.push(mp(0,1));
	while(!q.empty()) {
		int x = q.top().sec;q.pop();
		if(vis[x]) continue;
		vis[x] = 1;
		for(int i = head[x]; i; i = e[i].next) {
			int v = e[i].to;
			if(dis[v] > dis[x] + e[i].val) {
				dis[v] = dis[x] + e[i].val;
				if(!vis[v])
					q.push(mp(-dis[v], v));
			}
		}
	}
	int ans = 0x7fffffff;
	for(int i = 0; i <= k; ++i)
		ans = Min(ans, dis[i * n + n]);
	printf("%d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/do-while-true/p/13772996.html