Day4 网络流与二分图

news/2024/11/18 8:12:00/

之前那篇博客是在入门网络流时写的,现在对网络流重新有了一定的理解。

1. 最大流

FF 增广思想

Ford–Fulkerson 增广,核心即不断找增广路并增广。

dfs 实现

// FF brute
#include <bits/stdc++.h>
#define int long longusing namespace std;int n, m, s, t, r[205][205], ans;bool vis[205];
int dfs(int u, int sum) // sum:流入 u 的流量
{if(sum == 0 or u == t) return sum;vis[u] = true;int flow = 0;for(int i=1; i<=n; i++){if(!vis[i] and r[u][i] > 0){//找 u 到 t 路上的最小限制并增广int d = dfs(i, min(sum, r[u][i]));r[u][i] -= d, r[i][u] += d;flow += d, sum -= d;}}return flow;
}signed main()
{cin >> n >> m >> s >> t;for(int i=1; i<=m; i++){int u, v, w;cin >> u >> v >> w;r[u][v] += w;}int d;while(d = dfs(s, 1e9)) {memset(vis, 0, sizeof(vis));ans += d;}cout << ans;
}

这种方法的复杂度与最大流 f f f 有关,至多执行 f f f 轮 dfs,时间复杂度 O ( n f ) O(nf) O(nf)

bfs 实现 / EK

FF 增广的 bfs 实现也称 EK 算法。

// FF EK#include <bits/stdc++.h>
#define int long longusing namespace std;int n, m, s, t, r[205][205], ans, d[205], pre[205];
queue <int> q;
bool bfs()
{memset(d, 0, sizeof(d)); memset(pre, 0, sizeof(pre));d[s] = 1, q.push(s); while(!q.empty()){int u = q.front(); q.pop();for(int i=1; i<=n; i++)if(d[i] == 0 and r[u][i] > 0)pre[i] = u, d[i] = d[u] + 1, q.push(i);}return d[t];
}int augment()
{int flow = 1e9;for(int a=t; a!=s; a=pre[a]) flow = min(flow, r[pre[a]][a]);for(int a=t; a!=s; a=pre[a]) r[pre[a]][a] -= flow, r[a][pre[a]] += flow;return flow;
}signed main()
{cin >> n >> m >> s >> t;for(int i=1; i<=m; i++){int u, v, w;cin >> u >> v >> w;r[u][v] += w;}while(bfs()) ans += augment();cout << ans;
}

EK 算法的时间复杂度是 O ( n m 2 ) O(nm^2) O(nm2)

证明:每条边至多做 n 2 \dfrac n 2 2n 次增广路上的关键边。
OI-wiki

Dinic

通过 bfs 对图分层标记,每次 dfs 只访问下一层的结点。
同时加上当前弧优化,即不重复访问满流边,维护第一条有必要尝试的边。

// dinic#include <bits/stdc++.h>
#define int long longusing namespace std;struct Edge{int nxt, to, r;
}e[10005];int tot = 1, head[205];
void add_edge(int u, int v, int w)
{e[++tot] = {head[u], v, w};head[u] = tot;
}int n, m, s, t, ans, d[205], cur[205];queue <int> q;
bool bfs()
{memset(d, 0, sizeof(d)); d[s] = 1, q.push(s); while(!q.empty()){int u = q.front(); q.pop();for(int i=head[u]; i; i=e[i].nxt){int v = e[i].to;if(d[v] == 0 and e[i].r > 0)d[v] = d[u] + 1, q.push(v);}}return d[t];
}int dfs(int u, int sum)
{if(sum == 0 or u == t) return sum;int flow = 0;for(int i=cur[u]; i; i=e[i].nxt){cur[u] = i;int v = e[i].to;if(d[v] == d[u] + 1 and e[i].r > 0){int d = dfs(v, min(sum, e[i].r));e[i].r -= d, e[i^1].r += d;flow += d, sum -= d;}}return flow;
}signed main()
{cin >> n >> m >> s >> t;for(int i=1; i<=m; i++){int u, v, w;cin >> u >> v >> w;add_edge(u, v, w);add_edge(v, u, 0);}while(bfs()) {for(int i=1; i<=n; i++) cur[i] = head[i];ans += dfs(s, 1e9);}cout << ans;
}

2. 二分图最大匹配

  • 二分图:对于 G ( V , E ) G(V,E) G(V,E),分成两个点集 V 1 , V 2 V1,V2 V1,V2 ,对于所有的 ( u , v ) ∈ E (u,v) \in E (u,v)E, 保证 u , v u,v u,v 属于不同点集。

    容易发现二分图中不存在奇环。

  • 二分图的匹配:选定一些边,这些边之间没有公共点。

建网络流模型即可,通过虚拟源点和虚拟汇点限制 1 1 1

如果要输出方案,可以通过 f ( u , v ) < c ( u , v ) f(u,v) < c(u,v) f(u,v)<c(u,v)判断。如果根据残量网络,需要根据反向边的残量网络判断。

#include <bits/stdc++.h>
using namespace std;int n, m, s, t, r[105][105], ans, d[105], cur[105];
queue <int> q;
bool bfs()
{memset(d, 0, sizeof(d)); d[s] = 1, q.push(s); while(!q.empty()){int u = q.front(); q.pop();for(int i=1; i<=n+2; i++)if(d[i] == 0 and r[u][i] > 0)d[i] = d[u] + 1, q.push(i);}return d[t];
}int dfs(int u, int sum)
{if(sum == 0 or u == t) return sum;int flow = 0;for(int v=cur[u]; v<=n+2; v++){cur[u] = v;if(d[v] == d[u] + 1 and r[u][v] > 0){int d = dfs(v, min(sum, r[u][v]));r[u][v] -= d, r[v][u] += d;flow += d, sum -= d;}}return flow;
}int main()
{ios::sync_with_stdio(false);cin.tie(0);cin >> m >> n;s = n+1, t = n+2;while(1){int u, v;cin >> u >> v;if(u == -1 and v == -1) break;r[u][v] = 1;}for(int i=1; i<=m; i++) r[s][i] = 1;for(int i=m+1; i<=n; i++) r[i][t] = 1;while(bfs()) {for(int i=1; i<=n+2; i++) cur[i] = 1;ans += dfs(s, 1e9);}cout << ans << "\n";for(int u=1; u<=m; u++){for(int v=m+1; v<=n; v++){if(r[v][u]) cout << u << " " << v << "\n";}}	return 0;
}

3. 最大权闭合子图

分成正点和负电,把不选正点看作损失,每一种割对应一种方案代表不选。

答案即为正收益减去最小损失,即最小割,根据最小割最大流,即最大流。

具体可以看我之前的博客 浅谈网络流。


A. CF1783F Double Sort II

排列建图的套路。

先只考虑一个数列:记 i i i 所在位置 p i p_i pi,将 i → p i i \to p_i ipi 建边,形成若干个环,每次操作可以让环上少一个点,因此最小操作次数为 n n n 减去环的数量。

如果从反面出发,想最多能保留几个点可以不动,省操作次数,能保留的数量就是环的数量。

考虑两个数列:对于一个环,最多被省一个点。对于一个点,最多被省一次。

i i i 所在的两个环建边跑二分图最大匹配即可。


http://www.ppmy.cn/news/879089.html

相关文章

QT打开和保存文件对话框的操作笔记

QT打开和保存文件对话框的操作&#xff0c;需要先包含头文件QFileDialog&#xff0c;一般通过按钮实现打开和保存文件对话框的操作。 代码如下&#xff1a; #include <QDebug> #include <QFileDialog>void Form::on_pushButton_clicked() {QString fileName;fileN…

修复录音笔或其它录音设备损坏的WAV/MP3录音文件或0kb字节文件

由于录音笔等录音类数码产品长时间录音的自身可靠性和人为原因导致的录音文件损坏情况、例如以下几种情况&#xff1a;一、录音笔在录音过程中电量不足断电、卡机死机、录音结束忘记保存或强行关机都会导致录音文件wav损坏、无法播放、或是显示0kb字节&#xff1b;二、录音笔自…

在一种特殊情况下损坏了wav音频文件,修复的方法

几年前用某软件无损调整机械硬盘4k对齐时&#xff0c;手贱用别的软件改了盘符(因为4k对齐等待的时间太久&#xff0c;觉得无聊&#xff0c;加上以为那磁盘被占用了&#xff0c;反正改不动的&#xff0c;就改着玩)&#xff0c;导致进程异常中断了。磁盘里大量文件损坏。猜测是文…

EasyRecovery如何恢复wav音频文件

EasyRecovery能够恢复mp3和mpeg这些音频格式&#xff0c;可是你知道吗&#xff1f;EasyRecovery也能恢复到wav音频文件&#xff0c;而且操作简单&#xff0c;不需要复杂的步骤。下面小编就教大家如何在windows 10 系统下使用EasyRecovery14专业版恢复wav音频文件。 Eayrecover…

视频文件损坏无需再苦恼!快速修复方法分享!

如今录制视频或者从互联网下载视频都很简单&#xff0c;这些视频可以从笔记本电脑、电视甚至智能手机上用于观看或上传到自媒体平台/社交平台。 但视频有时会出现损坏的问题&#xff0c;导致视频无法正常播放&#xff0c;出现这种情况怎么办&#xff1f; 导致视频文件损坏的原…

UnDistort Audio File(音频修复软件)官方正式版V1.0 | 音频修复软件哪个好用 | 专业修复音频的软件

​ UnDistort Audio File 是一款短小精悍且非常实用的专业音频修复软件&#xff0c;支持多声道音频文件&#xff0c;使用可调检测参数自动检测音频文件中的失真&#xff0c;可在示例显示屏上逐一显示每个检测到的毛刺&#xff0c;通过自动或手动的方式修复目标…

MP3故障及原因

一 不开机。 1&#xff0e;按开机键无任何反应 &#xff08;1&#xff09;能正常联机 固件&#xff0c;按键&#xff0c;闪存格式&#xff0c;MP3文件。 &#xff08;2&#xff09;不能正常联机 硬件出了问题的多&#xff0c;还有一部份是固件不对 2&#xff0e;有开机动作&a…

文件损坏怎么修复?3种方法帮您恢复损坏的文件

案例&#xff1a;文件损坏还可以修复吗&#xff1f;文件损坏怎么修复&#xff1f; “救命&#xff01;&#xff01;&#xff01;我的视频文件系统损坏&#xff0c;且没有办法读取。试了很多方法&#xff0c;但是都没有成功。想问各位大神&#xff0c;还有什么好用的方法吗&…