平面图最小割
参考(准确来说差不多是转载):
- https://blog.csdn.net/m0_51780913/article/details/122410037 (平面图最小割)
- https://www.cnblogs.com/alex-wei/p/basic_graph_theory.html 中的平面图最小割部分(只说了网格图最小割)
基础定义
- 平面图:能画在平面上,满足除顶点处以外无边相交的图称为 平面图。
- 网格图:形成一个网格的平面图称为 网格图。
- 下面就是平面图和网格图的例子:
欧拉公式
(蒟蒻不太懂,可能这个公式可以用来证复杂度?)
对于一个
https://oi-wiki.org/graph/planar/#欧拉公式 中有很多推论。
平面图最小割
直接去看 https://blog.csdn.net/m0_51780913/article/details/122410037 吧
对偶图:对于平面图的一条边,两侧的两个面之间连一条边,边权为这条边的边权,得到平面图的对偶图。
求平面图的最小割:在
顺一个例子:
可以性感理解一下……
应用
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}), 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 (只给出了步骤,没有给出每一步的具体证明)
(不太会讲,直接去看视频更好)
可以网络流建模直接做,但是无法通过。接下来考虑正确做法。
把附加点的射线无线延伸,得到对偶图,类似这样:
感性理解有:按照顺序依次分为 ABABAB
,实际是要求两两之间走一条路,求最小的配对权值。
可以跑费用流,但是还有个性质:每一条路径之间一定可以做到无交。所以可以断环成链区间 dp。
代码
虽然在 C++20 O2 的加持下最慢的点跑了刚好 3s,不过没关系,能过就行
#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] 海拔
去看原题面去。
做法
非常弱智的转化后就是板子 平面图求最小割。代码
#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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步