行路难,行路难,多歧路,今安在?长风破浪会有时,直挂云|

hhhqx

园龄:1年8个月粉丝:9关注:13

平面图最小割

参考(准确来说差不多是转载):


基础定义

  • 平面图:能画在平面上,满足除顶点处以外无边相交的图称为 平面图
  • 网格图:形成一个网格的平面图称为 网格图
  • 下面就是平面图和网格图的例子:

欧拉公式

(蒟蒻不太懂,可能这个公式可以用来证复杂度?)

对于一个 n 个点 m 条边的平面图,设有 f 个面(包含无限延伸的那个面),有 f=mn+2

https://oi-wiki.org/graph/planar/#欧拉公式 中有很多推论。

平面图最小割

直接去看 https://blog.csdn.net/m0_51780913/article/details/122410037

对偶图:对于平面图的一条边,两侧的两个面之间连一条边,边权为这条边的边权,得到平面图的对偶图。

求平面图的最小割:在 ST 之间连一条新边,得到这个平面图的对偶图(被新边分开的两个面之间不连边),然后就是求两个平面之间的最短路(这两个平面,就是新边分开的两个面)

顺一个例子:image

可以性感理解一下……

应用

luogu - P4001 [ICPC-Beijing 2006] 狼抓兔子

题意:给定一个特定的平面图,S,T 分别在左上角和右下角,求最小割。

做法

直接做。

代码
#include <bits/stdc++.h>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
using PLI = pair<LL, int>;

const int MAXN = 3e6 + 3;

int n, m, S, T;
vector<PII> eg[MAXN];
LL dis[MAXN];
priority_queue<PLI, vector<PLI>, greater<PLI>> pq;

inline void ADDeg(int U, int V, int W){
	eg[U].push_back({V, W}), eg[V].push_back({U, W});
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m, S = (n - 1) * (m - 1) * 2 + 1, T = S + 1;
	for(int i = 1, L = (m - 1) * 2; i <= n; i++){
		for(int j = 1, w; j < m; j++){
			cin >> w;
			ADDeg((i == 1 ? S : (i - 2) * L + j * 2 - 1), (i == n ? T : (i - 1) * L + j * 2), w);
		}
	}
	for(int i = 1, L = (m - 1) * 2; i < n; i++){
		for(int j = 1, w; j <= m; j++){
			cin >> w;
			ADDeg((j == m ? S : (i - 1) * L + j * 2 - 1), (j == 1 ? T : (i - 1) * L + (j - 1) * 2), w);
		}
	}
	for(int i = 1, L = (m - 1) * 2; i < n; i++){
		for(int j = 1, w; j < m; j++){
			cin >> w;
			ADDeg((i - 1) * L + j * 2 - 1, (i - 1) * L + j * 2, w);
		}
	}
	n = T;
	for(int i = 1; i <= n; i++) dis[i] = 1e18;
	dis[S] = 0, pq.push({0, S});
	while(!pq.empty()){
		PLI i = pq.top(); pq.pop();
		if(dis[i.second] < i.first) continue;
		for(PII e : eg[i.second]){
			LL nw = i.first + e.second, nx = e.first;
			if(dis[nx] > nw) dis[nx] = nw, pq.push({nw, nx});
		}
	}
	cout << dis[T];
  return 0;
}

luogu - P7916 [CSP-S 2021] 交通规划

去看原题面去。

做法

参考:https://www.bilibili.com/video/BV1vP4y1b7jx/?spm_id_from=333.1391.0.0&vd_source=6f4e931f6b63b379a41998153f448a5b (只给出了步骤,没有给出每一步的具体证明)

(不太会讲,直接去看视频更好)

可以网络流建模直接做,但是无法通过。接下来考虑正确做法。

把附加点的射线无线延伸,得到对偶图,类似这样:image

感性理解有:按照顺序依次分为 ABABAB,实际是要求两两之间走一条路,求最小的配对权值。

可以跑费用流,但是还有个性质:每一条路径之间一定可以做到无交。所以可以断环成链区间 dp。

代码

虽然在 C++20 O2 的加持下最慢的点跑了刚好 3s,不过没关系,能过就行image

#include <bits/stdc++.h>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
using PLI = pair<LL, int>;

const int MAXP = 3e5 + 3;
const int inf = 1e9;

struct Node1{
	int x, p, t;
}a[53];

int n, m, T, wa[503][503], wb[503][503];
int D = 0, b[53];
vector<PII> eg[MAXP];

inline void ckmin(int &x, int y){ x = min(x, y); }
inline void ADDeg(int U, int V, int W){ eg[U].push_back({V, W}), eg[V].push_back({U, W}); }

LL dis[MAXP];
priority_queue<PLI, vector<PLI>, greater<PLI>> pq;
void Dijkstra(int S, int sum){
	for(int i = 1; i <= sum; i++) dis[i] = inf;
	while(!pq.empty()) pq.pop();
	dis[S] = 0, pq.push({0, S});
	while(!pq.empty()){
		PLI i = pq.top(); pq.pop();
		if(dis[i.second] < i.first) continue;
		for(PII e : eg[i.second]){
			LL nx = e.first, nw = i.first + e.second;
			if(dis[nx] > nw) dis[nx] = nw, pq.push({nw, nx});
		}
	}
}

int ew[105][105], f[105][105];

int main(){
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m >> T;
	for(int i = 1; i < n; i++){
		for(int j = 1; j <= m; j++) cin >> wa[i][j];
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j < m; j++) cin >> wb[i][j];
	}
	int tmp = (n - 1) * (m - 1), line = m - 1;
	while(T--){
		int k; cin >> k;
		for(int i = 1; i <= k; i++) cin >> a[i].x >> a[i].p >> a[i].t;
		bool ooo0 = 0, ooo1 = 0;
		for(int i = 1; i <= k; i++){
			if(a[i].t == 0) ooo0 = 1;
			else ooo1 = 1;
		}
		if(!ooo0 || !ooo1){
			cout << 0 << "\n";
			continue;
		}

		sort(a + 1, a + 1 + k, [](Node1 i, Node1 j){ return i.p < j.p; });
		for(int i = 1; i <= tmp + k; i++) eg[i].clear();
		a[k + 1] = a[1], D = 0;
		for(int i = 1; i <= k; i++){
			int U = i, V = (i == 1 ? k : i - 1);
			ADDeg(tmp + U, tmp + V, a[i].x);
			if(a[i].t != a[i + 1].t) b[++D] = tmp + U;
			int l = a[i].p, r = a[i + 1].p;
			for(int j = l; j != r; j = (j == 2 * m + 2 * n ? 1 : j + 1)){ int _j = 0;
				if(1 <= j && j < m) ADDeg(tmp + U, j, wb[1][j]);
				if(m + 1 <= j && j < m + n) _j = j - m, ADDeg(tmp + U, (_j - 1) * line + (m - 1), wa[_j][m]);
				if(m + n + 1 <= j && j < 2 * m + n) _j = 2 * m + n - j, ADDeg(tmp + U, (n - 2) * line + _j, wb[n][_j]);
				if(2 * m + n + 1 <= j && j < 2 * m + 2 * n) _j = 2 * m + 2 * n - j, ADDeg(tmp + U, (_j - 1) * line + 1, wa[_j][1]);
			}
		}
		for(int i = 1; i < n; i++){
			for(int j = 2; j < m; j++) ADDeg((i - 1) * line + j, (i - 1) * line + j - 1, wa[i][j]);
		}
		for(int i = 2; i < n; i++){
			for(int j = 1; j < m; j++) ADDeg((i - 1) * line + j, (i - 2) * line + j, wb[i][j]);
		}

		for(int i = 1; i <= D; i++){
			Dijkstra(b[i], tmp + k);
			for(int j = 1; j <= D; j++) ew[i][j] = dis[b[j]];
		}
		for(int i = 1; i <= D * 2; i++) for(int j = 1; j <= D * 2; j++) f[i][j] = inf;
		for(int i = 1; i <= D * 2; i++) f[i][i - 1] = 0;
		for(int len = 2; len <= D; len++){
			for(int i = 1; i <= D * 2 - len + 1; i++){
				int j = i + len - 1;
				ckmin(f[i][j], f[i + 1][j - 1] + ew[(i-1)%D+1][(j-1)%D+1]);
				for(int h = i; h < j; h++) ckmin(f[i][j], f[i][h] + f[h+1][j]);
			}
		}
		int ans = inf;
		for(int i = 1; i <= D; i++) ckmin(ans, f[i][i + D - 1]);
		cout << ans << "\n";
	}
	return 0;
}

luogu - P2046 [NOI2010] 海拔

去看原题面去。

做法 非常弱智的转化后就是板子 平面图求最小割。

luogu - P4001 [ICPC-Beijing 2006] 狼抓兔子 一样。

代码
#include <bits/stdc++.h>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
using PLI = pair<LL, int>;

const int MAXN = 3e6 + 3;

int n, m, S, T;
vector<PII> eg[MAXN];
LL dis[MAXN];
priority_queue<PLI, vector<PLI>, greater<PLI>> pq;

inline void ADDeg(int U, int V, int W){
	eg[U].push_back({V, W});
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
	cin >> n, n++, m = n, S = (n - 1) * (m - 1) + 1, T = S + 1;
	for(int i = 1, L = (m - 1); i <= n; i++){
		for(int j = 1, w; j < m; j++){
			cin >> w;
			ADDeg((i == 1 ? S : (i - 2) * L + j), (i == n ? T : (i - 1) * L + j), w);
		}
	}
	for(int i = 1, L = (m - 1); i < n; i++){
		for(int j = 1, w; j <= m; j++){
			cin >> w;
			ADDeg((j == m ? S : (i - 1) * L + j), (j == 1 ? T : (i - 1) * L + j - 1), w);
		}
	}
	for(int i = 1, L = (m - 1); i <= n; i++){
		for(int j = 1, w; j < m; j++){
			cin >> w;
			ADDeg((i == n ? T : (i - 1) * L + j), (i == 1 ? S : (i - 2) * L + j), w);
		}
	}
	for(int i = 1, L = (m - 1); i < n; i++){
		for(int j = 1, w; j <= m; j++){
			cin >> w;
			ADDeg((j == 1 ? T : (i - 1) * L + j - 1), (j == m ? S : (i - 1) * L + j), w);
		}
	}
	n = T;
	for(int i = 1; i <= n; i++) dis[i] = 1e18;
	dis[S] = 0, pq.push({0, S});
	while(!pq.empty()){
		PLI i = pq.top(); pq.pop();
		if(dis[i.second] < i.first) continue;
		for(PII e : eg[i.second]){
			LL nw = i.first + e.second, nx = e.first;
			if(dis[nx] > nw) dis[nx] = nw, pq.push({nw, nx});
		}
	}
	cout << dis[T];
  return 0;
}

本文作者:hhhqx

本文链接:https://www.cnblogs.com/huangqixuan/p/18640092

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hhhqx  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起