『网络流』学习笔记 之 最大流

定义

        网络:一张带权有向图,可以类比为输水系统

        源点:可以提供无穷大流量的出发点,可以类比为大水库

        源点要到达汇点,要经过许多中转点,中转点不存储流量,流入和与流出和相等。每条边的流量不允许超过该边的权值,这样限定了最大流不允许为+\infty

题目链接:P3376 【模板】网络最大流

算法概述

 先来看这么一张图,源点是S,目的地为T

 我们随机选则一条路,如:S->1->2->T,但显然最大流为1(S->1->T与S->2->T)

对于这种情况,可以考虑“撤销” 1 到 2 的操作,建一条大小为流量的反向边,换一条路,使总贡献增加

增广路:从源点开始,顺着有流量的边,能到达汇点,会使网络总流量增加的路

而求解最大流的算法就是不断寻找增广路,直到无法再增加为止。

考虑用爆搜寻找增广路(FF算法),显然会炸,需要优化

Dinic 算法

  • 每次多路增广,若u点到v点的流量没有用完,可以考虑把剩下的流量贡献给其它u指向的点
  • bfs分层,考虑回溯时可能会出现绕远或绕回,按照与源点的距离将图分层(bfs实现)

    sol002.gif

 AC code

#include<bits/stdc++.h>
#define INF 0x7fffffff/3
#define reg register
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define IN freopen(".in.txt","r",stdin)
#define OUT freopen(".out.txt","w",stdout)
#define LL long long
using namespace std;
const int N=5e3+10;
int n,m,s,t,cnt=1,head[N];
queue<int> q;
int dep[N];
struct EDGE{
	int pre,to;
	long long val;
}edge[N<<1];
inline long long read(){
	long long k=1,x=0;char ch=getchar();
	while (ch<'0'||ch>'9') {if (ch=='-') k=-1; ch=getchar();}
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
	return k*x;
}
inline void add(int u,int v,long long w){
	edge[++cnt]=(EDGE){head[u],v,w};
	head[u]=cnt;
} 
inline bool bfs(){ //寻找是否还有增广路
	memset(dep,0,sizeof(dep));
	dep[s]=1,q.push(s);
	while (!q.empty()){
		int u=q.front(); q.pop();
		for (int i=head[u];i;i=edge[i].pre){
			int v=edge[i].to;
			if (edge[i].val&&!dep[v]) dep[v]=dep[u]+1,q.push(v); //搜还可以贡献流量的边 
		}
	} 
	return dep[t]; //若 t 还可以被更新,说明还有增广路 
}

inline long long dfs(int u,long long flow){
	if (u==t) return flow;
	long long dout=0;
	for (int i=head[u];i&&flow;i=edge[i].pre){
		int v=edge[i].to;
		if (edge[i].val&&dep[v]==dep[u]+1){//限制只能流向下一层
			long long res=dfs(v,min(flow,edge[i].val));
			edge[i].val-=res;
			edge[i^1].val+=res;  //更新反向边的权值
			flow-=res;
			dout+=res; 
		}
	}
	if (!dout) dep[u]=0; //与汇点不连通,打上标记,之后不再更新
	return dout; 
}
int main(){
	n=read(),m=read(),s=read(),t=read();
	for (long long i=1,u,v,w;i<=m;i++) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,0);
	long long ans=0;
	while (bfs()) ans+=dfs(s,1e18); //源点流量无限大
	printf("%lld",ans); 
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值