当前位置: 首页 > news >正文

【笔记】网络流算法模板

文章目录


EK求最大流

题目描述

给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量,边的容量非负。

图中可能存在重边和自环。求从点 S S S 到点 T T T 的最大流。

输入格式

第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T

接下来 m m m 行,每行三个整数 u , v , c u,v,c u,v,c,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c

点的编号从 1 1 1 n n n

输出格式

输出点 S S S 到点 T T T 的最大流。

如果从点 S S S 无法到达点 T T T 则输出 0 0 0

数据范围

2 ≤ n ≤ 1000 2 \le n \le 1000 2n1000,
1 ≤ m ≤ 10000 1 \le m \le 10000 1m10000,
0 ≤ c ≤ 10000 0 \le c \le 10000 0c10000,
S ≠ T S \neq T S=T


算法步骤

不断 BFS,用 while 循环不断找残量网络中的增广路径。

每次

  1. 找到增广路径
  2. 更新残量网络
  3. 累加最大流量

跳出循环即得出最大流。

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

AC Code

C + + \text{C}++ C++

#include <iostream>
#include <cstring>using namespace std;const int N = 1010, M = 20010;
const int INF = 1e9;int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], pre[N];
bool st[N];inline 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);int hh = 0, tt = 0;q[0] = S, d[S] = INF, st[S] = true;while (hh <= tt){int t = q[hh ++ ];for (int i = h[t]; ~i; i = ne[i]){int j = e[i];if (!st[j] && f[i]){st[j] = true;d[j] = min(d[t], f[i]);pre[j] = i;if (j == T) return true;q[ ++ tt] = j;}}}return false;
}int EK()
{int flow = 0;while (bfs()){flow += d[T];for (int i = T; i != S; i = e[pre[i] ^ 1])f[pre[i]] -= d[T], f[pre[i] ^ 1] += d[T]; }return flow;
}int main()
{int a, b, c;memset(h, -1, sizeof h);scanf("%d%d%d%d", &n, &m, &S, &T);while (m -- ){scanf("%d%d%d", &a, &b, &c);add(a, b, c);}printf("%d\n", EK());return 0;
}

Dinic/ISAP求最大流

题目描述

给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量,边的容量非负。

图中可能存在重边和自环。求从点 S S S 到点 T T T 的最大流。

输入格式

第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T

接下来 m m m 行,每行三个整数 u , v , c u,v,c u,v,c,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c

点的编号从 1 1 1 n n n

输出格式

输出点 S S S 到点 T T T 的最大流。

如果从点 S S S 无法到达点 T T T 则输出 0 0 0

数据范围

2 ≤ n ≤ 10000 2 \le n \le 10000 2n10000,
1 ≤ m ≤ 100000 1 \le m \le 100000 1m100000,
0 ≤ c ≤ 10000 0 \le c \le 10000 0c10000,
S ≠ T S \neq T S=T


算法步骤

Dinic 算法其实是 EK 算法的一个暴力的优化,EK 算法每次只能搜索一条增广路径,而 Dinic 算法每次都用 DFS 的形式尽可能多的搜索增广路径。

而图中可能存在环,为了保证 DFS 的过程中不会造成死循环,这里可以使用分层图,这样每次都是一层一层往下搜索,就不会出现死循环。

  1. BFS 建立分层图
  2. DFS 找出所有能增广的路径
  3. 累加最大流量

注意: Dinic 算法对于优化非常敏感,如果优化的不好就可能直接 TLE

算法时间复杂度 O ( n 2 m ) O(n^2m) O(n2m)

AC Code

C + + \text{C}++ C++

#include <iostream>
#include <cstring>using namespace std;const int N = 10010, M = 200010;
const int INF = 1e9;int n, m, S, T;
int h[N], e[M], ne[M], f[M], idx;
int d[N], q[N], cur[N];
bool st[N];inline 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);int hh = 0, tt = 0;q[0] = S, d[S] = 0, cur[S] = h[S];while (hh <= tt){int t = q[hh ++ ];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[ ++ tt] = j;}}}return false;
}int find(int u, int lim)
{if (u == T) return lim;int flow = 0;for (int i = cur[u]; ~i && flow < lim; cur[u] = i, i = ne[i]){int j = e[i];if (d[j] == d[u] + 1 && f[i]){int t = find(j, min(f[i], lim - flow));if (!t) d[j] = -1;f[i] -= t, f[i ^ 1] += t, flow += t;}}return flow;
}int dinic()
{int r = 0, flow = 0;while (bfs()) while ((flow = find(S, INF))) r += flow;return r;
}int main()
{int a, b, c;memset(h, -1, sizeof h);scanf("%d%d%d%d", &n, &m, &S, &T);while (m -- ){scanf("%d%d%d", &a, &b, &c);add(a, b, c);}printf("%d\n", dinic());return 0;
}

Dinic/ISAP求最小割

根据 最大流最小割定理,我们知道,最大流与最小割在数值上相等。证明过程见 这里。

因此跑最大流的 Dinic 板子即可,代码没有任何区别,就不再放了。


EK求费用流

题目描述

给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量和费用,边的容量非负。

图中可能存在重边和自环,保证费用不会存在负环。

求从 S S S T T T 的最大流,以及在流量最大时的最小费用。

输入格式

第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T

接下来 m m m 行,每行三个整数 u , v , c , w u,v,c,w u,v,c,w,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c,费用为 w w w

点的编号从 1 1 1 n n n

输出格式

输出点 S S S 到点 T T T 的最大流和流量最大时的最小费用。

如果从点 S S S 无法到达点 T T T 则输出 0 0

数据范围

2 ≤ n ≤ 5000 2≤n≤5000 2n5000,
1 ≤ m ≤ 50000 1≤m≤50000 1m50000,
0 ≤ c ≤ 100 0≤c≤100 0c100,
− 100 ≤ w ≤ 100 -100 \le w \le 100 100w100
S ≠ T S≠T S=T


算法步骤

费用流算法本质上是 EK 算法,只不过将找增广路的 BFS 算法替换为了 SPFA 算法。

  1. 找到增广路径
  2. 更新残量网络
  3. 累加最大流量
算法时间复杂度 O ( n m 2 ) O(nm^2) O(nm2)

AC Code

C + + \text{C}++ C++

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 5010, M = 100010, INF = 1e8;int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];void add(int a, int b, int c, int d)
{e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}bool spfa()
{int hh = 0, tt = 1;memset(d, 0x3f, sizeof d);memset(incf, 0, sizeof incf);q[0] = S, d[S] = 0, incf[S] = INF;while (hh != tt){int t = q[hh ++ ];if (hh == N) hh = 0;st[t] = false;for (int i = h[t]; ~i; i = ne[i]){int ver = e[i];if (f[i] && d[ver] > d[t] + w[i]){d[ver] = d[t] + w[i];pre[ver] = i;incf[ver] = min(f[i], incf[t]);if (!st[ver]){q[tt ++ ] = ver;if (tt == N) tt = 0;st[ver] = true;}}}}return incf[T] > 0;
}void EK(int& flow, int& cost)
{flow = cost = 0;while (spfa()){int t = incf[T];flow += t, cost += t * d[T];for (int i = T; i != S; i = e[pre[i] ^ 1]){f[pre[i]] -= t;f[pre[i] ^ 1] += t;}}
}int main()
{scanf("%d%d%d%d", &n, &m, &S, &T);memset(h, -1, sizeof h);while (m -- ){int a, b, c, d;scanf("%d%d%d%d", &a, &b, &c, &d);add(a, b, c, d);}int flow, cost;EK(flow, cost);printf("%d %d\n", flow, cost);return 0;
}

最后,如果觉得对您有帮助的话,点个赞再走吧!

相关文章:

  • CSS3新增样式
  • Gitlab仓库推送到Gitee仓库的一种思路
  • 腾讯云debian服务器的连接与初始化
  • 基于Java (spring-boot)的宠物管理系统
  • 【运维面试100问】(九)了解Raid嘛?
  • 【正点原子STM32连载】第十七章 通用定时器中断实验 摘自【正点原子】APM32E103最小系统板使用指南
  • Mysql的SQL优化和锁
  • C语言—每日选择题—Day59
  • Java基础题3:继承
  • Linux网络编程——概述、TCP/UDP的对比
  • 数据库(三)超详细SQL语句入门 | SQL增删改查,重命名,字符操作,联合操作,聚合函数,嵌套子查询
  • Oracle的学习心得和知识总结(三十)| OLTP 应用程序的合成工作负载生成器Lauca论文翻译及学习
  • UG扫掠体与部件导航器的使用
  • 数据管理平台Splunk Enterprise本地部署结合内网穿透实现远程访问
  • 计算机网络 网络层下 | IPv6 路由选择协议,P多播,虚拟专用网络VPN,MPLS多协议标签
  • [PHP内核探索]PHP中的哈希表
  • (三)从jvm层面了解线程的启动和停止
  • 【剑指offer】让抽象问题具体化
  • 2017年终总结、随想
  • 5、React组件事件详解
  • echarts的各种常用效果展示
  • Fundebug计费标准解释:事件数是如何定义的?
  • IDEA常用插件整理
  • in typeof instanceof ===这些运算符有什么作用
  • Java Agent 学习笔记
  • Java小白进阶笔记(3)-初级面向对象
  • nodejs:开发并发布一个nodejs包
  • Solarized Scheme
  • SpringBoot 实战 (三) | 配置文件详解
  • 工作手记之html2canvas使用概述
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 你不可错过的前端面试题(一)
  • 七牛云假注销小指南
  • 如何选择开源的机器学习框架?
  • 手写一个CommonJS打包工具(一)
  • 通过npm或yarn自动生成vue组件
  • 微信小程序--------语音识别(前端自己也能玩)
  • 我的业余项目总结
  • 在electron中实现跨域请求,无需更改服务器端设置
  • No resource identifier found for attribute,RxJava之zip操作符
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • #pragam once 和 #ifndef 预编译头
  • #pragma预处理命令
  • #宝哥教你#查看jquery绑定的事件函数
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (5)STL算法之复制
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)Oracle存储过程编写经验和优化措施
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'