网络流

网络流学习笔记

1|0概念

流网络:原图(无反向边)G=(V,E),起点为s,终点为t。记 c(u,v)uv 的流量限制。

可行流:记 f(u,v)uv 之间的流量。则可行流 f 满足以下条件:

  • 容量限制:0f(u,v)c(u,v)
  • 流量守恒:f(v,x)=f(x,u)

最大流:最大可行流

残量网络:和可行流对应。记为 GfVf=V,Ef=EE
c(u,v)={c(u,v)f(u,v)(u,v)Ef(v,u)(v,u)E

增广路径:在残量网络里从起点能达到终点的一条路径。

割:把V分为两个集合S,T,满足ST=V,ST=

  • 容量c(S,T):两个集合之间的容量之和。
  • 流量f(S,T):对于一种可行流,两个集合之间的流量之和。
  • 对于任意可行流f:|f|=f(u,v)c(u,v)
  • 最小割:最小割的容量
  • 最大流等于最小割

1|1EK

在残量网络中寻找一条增广路,将流量尽可能地流入这条增广路。
重复以上步骤直到没有增广路。
时间复杂度:O(|V||E|2),证明见OI Wiki

1|2Dinic

对 EK 进行改进,我们想每次找多条增广路。为了避免形成环,找增广路前需要先对图进行分层,然后再在分层图中找增广路。
时间复杂度:O(|V|2|E|),证明见OI Wiki
实际上会比理论上快很多。

int head[N], vi[M], wi[M], ne[M], tot = 1, dis[N], s, t; queue <int> q; bool bfs(){ while(!q.empty()) q.pop(); memset(dis, -1, sizeof(dis)); q.push(s); dis[s] = 0; while(!q.empty()){ int x = q.front(); q.pop(); for(int i = head[x]; i; i = ne[i]){ int y = vi[i]; if(dis[y] == -1 && wi[i]){ dis[y] = dis[x] + 1; if(y == t) return 1; q.push(y); } } } return 0; } long long dfs(int x, long long lim){ if(x == t) return lim; long long flow = 0; for(int &i = cur[x]; i; i = ne[i]){ int y = vi[i]; if(dis[y] != dis[x] + 1 || !wi[i]) continue; long long t = dfs(y, min(1ll * wi[i], lim - flow)); if(!t) dis[y] = -1; wi[i] -= t, wi[i ^ 1] += t, flow += t; if(flow == lim) break; } return flow; } long long dinic(){ long long sum = 0, flow; while(bfs()){ memcpy(cur, head, sizeof(head)); while(flow = dfs(s, INF)) sum += flow; } return sum; }

1|3费用流

int head[N], vi[M], wi[M], pr[M], ne[M], tot = 1; int ss, tt, dis[N], cur[N]; bool inq[N], vis[N]; queue <int> q; bool spfa(){ memset(dis, 0x3f, sizeof(dis)); memset(inq, 0, sizeof(inq)); while(!q.empty()) q.pop(); dis[ss] = 0, inq[ss] = 1; q.push(ss); while(!q.empty()){ int x = q.front(); q.pop(); inq[x] = 0; for(int i = head[x]; i; i = ne[i]){ int y = vi[i]; if(dis[y] > dis[x] + pr[i] && wi[i]){ dis[y] = dis[x] + pr[i]; if(!inq[y]){ q.push(y); inq[y] = 1; } } } } return dis[tt] != dis[0]; } int dfs(int x, int lim, int &cst){ if(x == tt) return lim; vis[x] = 1; int flow = 0; for(int &i = cur[x]; i; i = ne[i]){ int y = vi[i]; if(dis[y] != dis[x] + pr[i] || !wi[i] || vis[y]) continue; int dlt = dfs(y, min(lim - flow, wi[i]), cst); flow += dlt, cst += pr[i] * dlt, wi[i] -= dlt, wi[i ^ 1] += dlt; if(flow == lim) break; } vis[x] = 0; return flow; } int dinic(){ int sum = 0, flow, cst = 0; while(spfa()){ memcpy(cur, head, sizeof(head)); while((flow = dfs(ss, INF, cst))) sum += flow; } return cst; }

__EOF__

本文作者louisliang
本文链接https://www.cnblogs.com/louisliang/articles/18106985.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   louisliang  阅读(15)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示