2023.4.5【图论】网络最大流 Dinic算法
网络最大流 Dinic算法
省选爆了qwq
题目描述
给出一个网络图,以及其源点和汇点,求出其网络最大流。
网络流,就像水在一个水渠构成的网络中流一样,源点有无限的水,每条边有最大流量限制,求流到汇点的最大流量。
更菜一点的EK算法自行了解,此处我们用dinic算法解决问题。
这些网络流算法的核心其实都是一样的(HLPP当我没说),就是每次通过bfs找出增广路,然后进行增广。
增广路:即源点到汇点整条路上边权都大于0的点,根据定义,这样的一条路上最小边就是更新的答案。
EK算法之所以低效,是因为它每次用bfs寻找一条增广路,然后进行增广,dinic算法就是通过一整个bfs,将所有可能的路加入分层图,然后用一个dfs进行统一增广,就显得要高效一些。
算法流程
bfs部分:
记录一个
dfs部分:
dfs记录当前到的点x和剩余流量flow,对于x,扫描每一条出边,如果这条出边不在分层图中就不管。扫描到每一个
这时有一个问题:万一增广错了怎么办?我们将
每次需要记录一个vis[x]数组,在开始递归时记为1,结束递归时计为0,为了避免“反复横跳”的情况发生。
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5,M = 5e4 + 5,inf = 0x3f3f3f3f;
struct Edge{
int v,w,next;
}e[M * 2];
int head[N],n,m,s,t,tot = 1,dep[N],dis[N],vis[N],now[N];
inline void add(int x,int y,int z)
{
++tot;
e[tot].v = y;
e[tot].w = z;
e[tot].next = head[x];
head[x] = tot;
++tot;
e[tot].v = x;
e[tot].w = 0;
e[tot].next = head[y];
head[y] = tot;
}
inline bool SPFA()
{
queue <int> q;
memset(dep,0,sizeof(dep));
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
while(!q.empty()) q.pop();
dis[s] = 0;
q.push(s);
now[s] = head[s];
while(!q.empty())
{
int x = q.front();
q.pop();
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].v;
if(e[i].w <= 0) continue;
if(dis[to] == inf)
{
dis[to] = dis[x] + 1;
now[to] = head[to];
q.push(to);
}
}
}
if(dis[t] != inf) return 1;
return 0;
}
inline int dinic(int x,int flow)
{
if(x == t) return flow;
int k,res = 0;
vis[x] = 1;
for(int i = now[x];i && flow;i = e[i].next)
{
now[x] = i;
int to = e[i].v;
if(e[i].w <= 0 || dis[to] != dis[x] + 1 || vis[to]) continue;
k = dinic(to,min(flow,e[i].w));
if(!k) dis[to] = inf;
e[i].w -= k;
e[i ^ 1].w += k;
flow -= k;
res += k;
}
vis[x] = 0;
return res;
}
int main()
{
cin>>n>>m>>s>>t;
int x,y,z;
for(int i = 1;i <= m;i++)
{
cin>>x>>y>>z;
add(x,y,z);
}
long long ans = 0;
while(SPFA())
ans += dinic(s,inf);
cout<<ans;
return 0;
}
最小费用最大流
这个“费用”,就是在每条边的流量之外,加入了一个单位流量费用,现在要求流量最大的同时费用最小。我们发现,一条增广路上的流量是一样的,所以增广路边权之和最小,费用就会最小,所以我们贪心地将bfs改成最短路即可,因为有负权边,所以使用SPFA,在每次记录流量的时候用一个全局变量记录费用
最大费用最大流同理,改为最长路。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5,M = 5e4 + 5,inf = 0x3f3f3f3f;
struct Edge{
int v,w,c,next;
}e[M * 2];
int head[N],n,m,s,t,tot = 1,dep[N],dis[N],vis[N],now[N];
long long ans2 = 0;
inline void add(int x,int y,int z,int cost)
{
++tot;
e[tot].v = y;
e[tot].w = z;
e[tot].c = cost;
e[tot].next = head[x];
head[x] = tot;
++tot;
e[tot].v = x;
e[tot].w = 0;
e[tot].c = -cost;
e[tot].next = head[y];
head[y] = tot;
}
queue <int> q;
inline bool SPFA()
{
memset(dep,0,sizeof(dep));
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
while(!q.empty()) q.pop();
dis[s] = 0;
q.push(s);
now[s] = head[s];
while(!q.empty())
{
int x = q.front();
q.pop();
vis[x] = 0;
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].v;
if(e[i].w <= 0) continue;
if(dis[to] > dis[x] + e[i].c)
{
dis[to] = dis[x] + e[i].c;
now[to] = head[to];
if(!vis[to])
{
vis[to] = 1;
q.push(to);
}
}
}
}
if(dis[t] != inf) return 1;
return 0;
}
inline int dinic(int x,int flow)
{
if(x == t) return flow;
int k,res = 0;
vis[x] = 1;
for(int i = now[x];i && flow;i = e[i].next)
{
now[x] = i;
int to = e[i].v;
if(e[i].w <= 0 || dis[to] != dis[x] + e[i].c || vis[to]) continue;
k = dinic(to,min(flow,e[i].w));
if(!k) dis[to] = inf;
e[i].w -= k;
e[i ^ 1].w += k;
flow -= k;
res += k;
ans2 += e[i].c * k;
}
vis[x] = 0;
return res;
}
int main()
{
cin>>n>>m>>s>>t;
int x,y,z,cost;
for(int i = 1;i <= m;i++)
{
cin>>x>>y>>z>>cost;
add(x,y,z,cost);
}
long long ans = 0;
while(SPFA())
{
memset(vis,0,sizeof(vis));
ans += dinic(s,inf);
}
cout<<ans<<" "<<ans2;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?