[luoguP3376] 网络最大流
题意
给出一个网络,求流量的最大值
sol
网络流板子题。
网络流是 OI 中比较常用的算法之一,以较高的建图难度深受出题人喜爱,不过近几年题目数量减少。
当然,在学习建图之前,需要先学会网络流的板子。
一些定义
(部分摘自 OI-Wiki)
网络是特殊的有向图 ;
源点 是网络的起点,汇点 是网络的终点,;
对于,其容量为 ,特别的,若 ,
对于,其流量为 ,且满足以下性质:
对于一个网络上的一个流,我们定义其流量
对于一个网络 ,若 且 且 ,则 {S,T} 是 的一个割,其容量
对于网络 ,其可能的最大的流量 即为 的最大流
Edmond-Karp
在残量网络中 BFS 找到一条增广路(从源点到汇点容量为正的路径),累加流量,最后更新残量网络。
理论复杂度上限 ,实际可处理 的数据
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef unsigned int UI;
typedef long long LL;
constexpr int N = 205, M = 10005;
constexpr UI INF = 1 << 31;
int h[N], e[M], f[M], ne[M], idx;
int pre[N];
UI d[N];
bool st[N];
int n, m, S, T;
void add(int a, int b, int c){
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool BFS(){
memset(st, false, sizeof st);
queue<int> q;
st[S] = true, d[S] = INF;
q.push(S);
while (!q.empty()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]){
int j = e[i];
if (!st[j] && f[i]) {
st[j] = true;
pre[j] = i;
d[j] = min(d[t], (UI) f[i]);
if (j == T) return true;
q.push(j);
}
}
}
return false;
}
LL EK(){
LL res = 0;
while (BFS()) {
res += d[T];
for (int u = T; u != S; u = e[pre[u] ^ 1])
f[pre[u]] -= d[T], f[pre[u] ^ 1] += d[T];
}
return res;
}
int main(){
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- ){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%lld\n", EK());
return 0;
}
Dinic
在残量网络中进行分层(防止出现环),然后爆搜每一条增广路,如果可行的话就累加
注意一部分优化:
- 在爆搜的过程中,如果发现当前节点的流出流量已经超过流入流量,则直接返回
- 在爆搜的过程中,如果发现某个节点无法提供任何流量,那么就在这个残量网络中删除这个节点
- 在爆搜的过程中,如果重复遍历某个点,那么继续上一次未搜索完的弧遍历(当前弧优化)
理论复杂度上限 ,实际可处理 的数据。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef unsigned int UI;
const int N = 1205, M = 240005;
const UI INF = 1 << 31;
int h[N], e[M], ne[M], idx;
UI f[M];
int d[N], cur[N];
int n, m, S, T;
void add(int a, int b, int c){
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs(){
memset(d, -1, sizeof d);
queue<int> q;
q.push(S), d[S] = 0, cur[S] = h[S];
while (!q.empty()) {
int t = q.front(); q.pop();
for (int i = h[t]; ~i; i = ne[i]){
int j = e[i];
if (d[j] == -1 && f[i]) {
d[j] = d[t] + 1;
cur[j] = h[j];
if (j == T) return true;
q.push(j);
}
}
}
return false;
}
UI find(int u, UI limit) {
if (u == T) return limit;
UI flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]){ // 优化 1
cur[u] = i; // 优化 3
int j = e[i];
if (d[j] == d[u] + 1 && f[i]) {
int t = find(j, min(limit - flow, f[i]));
if (!t) d[j] = -1; // 优化 2
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic(){
int res = 0, flow;
while (bfs()) while (flow = find(S, INF)) res += flow;
return res;
}
int main(){
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- ){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
}
Dinic 算法在大部分题目中都够用,因为卡 Dinic 是没有浮木的行为。
此外还有效率差不多的 ISAP 算法和相对更快的 HLPP 算法(理论上届复杂度 ),但是不会。
分类:
题解 / 2024训练
标签:
图论
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现