【模板】最大流Dinic算法

参考题目:loj101


解析:

先介绍几个概念(公式恐惧症慎入)。

流网络与流

流网络 G = ( V , E ) G=(V,E) G=(V,E)是一个有向图。并且对于 ∀ e ∈ E \forall e \in E eE,边 e e e都有一个非负的容量值 c a p [ e ] cap[e] cap[e]。而且如果 ( u , v ) ∈ E (u,v) \in E (u,v)E,则 ( v , u ) ∉ E (v,u) \notin E (v,u)/E ,且图中无自环。

对于 ( u , v ) ∉ E (u,v) \notin E (u,v)/E,定义 c a p [ ( u , v ) ] = 0 cap[(u,v)]=0 cap[(u,v)]=0

源点和汇点

在流网络中,我们单独抽出两个特殊节点。源点记为 s s s,汇点记为 t t t

流的定义

算法导论版:设 G = ( V , E ) G=(V,E) G=(V,E)是一个流网络。其容量函数为 c c c。设 s s s为网络的源节点, t t t为汇点。 G G G中的流是一个实值函数 f : V × V − > R f:V\times V->R f:V×V>R,满足以下性质:
容量限制:对于 ∀ e = ( u , v ) ∈ E \forall e=(u,v)\in E e=(u,v)E,都有 0 &lt; = f [ e ] &lt; = c a p [ e ] 0&lt;=f[e]&lt;=cap[e] 0<=f[e]<=cap[e]
流量守恒:对于 ∀ u ∈ V − { s , t } \forall u\in V-\{s,t\} uV{s,t},满足 ∑ v ∈ V f ( u , v ) = ∑ v ∈ V f ( v , u ) \sum_{v\in V}f(u,v)=\sum_{v\in V}f(v,u) vVf(u,v)=vVf(v,u)即源汇点以外的点,流入和流出的流量相同。

z x y o i zxyoi zxyoi感性理解版:定义 f ( u , v ) f(u,v) f(u,v) e = ( u , v ) e=(u,v) e=(u,v)这条边中的流,那么 f ( u , v ) f(u,v) f(u,v)满足以上两个限制。

而一个流 f ( G ) f(G) f(G)就是满足以上两个限制的一整个图的流的集合。注意流不一定只代表一条边的流。而是整张图的流的集合。 f ( G ) f(G) f(G)可以简记为 f f f。满足以上两个限制的流称为合法的流。

流量

定义 f f f的流量为 ∣ f ∣ = ∑ ( v ∈ V ) ∣ f ( s , v ) ∣ − ∑ v ∈ V ∣ f ( v , s ) ∣ |f|=\sum_{(v \in V)}|f(s,v)|-\sum_{v \in V}|f(v,s)| f=(vV)f(s,v)vVf(v,s)其中 s s s是该网络的源点。一般情况下,后面的式子值为0,因为一般不会有其他边流向源点,但是在某些情况下,这种式子却显得尤为重要。这里不予讨论,因为本博客主要是介绍最大流 D i n i c Dinic Dinic算法。

而我们希望找到一个流 f f f,使得在合法的情况下 ∣ f ∣ |f| f最大,这就是最大流。

那么对于最大流,我们已经有两个较为朴素的算法, F o r d − F u l l k e r s o n Ford-Fullkerson FordFullkerson E d m o n d s − K a r p Edmonds-Karp EdmondsKarp算法。但是效率不是很高。

先介绍一下一般的最大流算法。
首先明确两个概念。

残存网络和增广路

在已经找出一个流之后,定义边的残存容量为 c f ( u , v ) = c ( u , v ) − f ( u , v ) c_f(u,v)=c(u,v)-f(u,v) cf(u,v)=c(u,v)f(u,v)

定义 f ( G ) f(G) f(G) G = ( V , E ) G=(V,E) G=(V,E)的残存网络为 G f = ( V , E ′ ) G_f=(V,E&#x27;) Gf=(V,E),其中 E ′ = { e ′ ∣ e ∈ E , e ′ = e = ( u , v ) , c ( e ′ ) = c f ( e ) } E&#x27;=\{e&#x27;|e\in E,e&#x27;=e=(u,v),c(e&#x27;)=c_f(e)\} E={eeE,e=e=(u,v),c(e)=cf(e)}(其中包括原图的反向边)。我们一般做网络流问题都是直接在残存网络上做。

如果我们找出一条路径 s − &gt; t s-&gt;t s>t,并且路径上的剩余容量最小的边的容量大于0,那么我们就称 s − &gt; t s-&gt;t s>t可增广。

那么是不是一直沿着增广路径增广就能找到最大流呢?
不一定。
有时候的增广可能刚好将自己的路堵塞,使得部分不该空的边空了,该流的边流不动。
反例很多,读者可以自己试着找一下。

那么,怎么办?(待更新,写解析打数学公式真是一件令人愉快 而又痛苦的事)


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
	re int num=0;
	re char c;
	while(!isdigit(c=gc()));
	while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
	return num;
}

int n,m;
int last[102],nxt[10002],to[10002],ecnt=1;
ll cap[10002];

inline
void addedge(int u,int v,int val){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0;
}

int lev[102],cur[102];

inline
bool BFS(cs int &ss,cs int &tt){
	memset(lev,-1,sizeof lev);
	queue<int >q;
	q.push(ss);
	cur[ss]=last[ss];
	lev[ss]=0;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
			if(lev[v]==-1&&cap[e]){
				lev[v]=lev[u]+1;
				if(v==tt)return true;
				cur[v]=last[v];
				q.push(v);
			}
		}
	}
	return false;
}

inline
ll Dinic(int u,cs ll &flow,cs int &t){
	if(u==t)return flow;
	ll ans=0;
	for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
		if(cap[e]&&lev[u]<lev[v]){
			ll delta=Dinic(v,min(flow-ans,cap[e]),t);
			if(delta){
				cap[e]-=delta;
				cap[e^1]+=delta;
				ans+=delta;
				if(ans==flow)return flow;
			}
		}
	}
	lev[u]=-1;
	return ans;
}

inline
ll maxflow(cs int &ss,cs int &tt){
	ll ans=0;
	while(BFS(ss,tt))ans+=Dinic(ss,1ll<<40,tt);
	return ans;
}

signed main(){
	n=getint();
	m=getint();
	int s=getint();
	int t=getint();
	for(int re i=1;i<=m;++i){
		int u=getint(),v=getint(),val=getint();
		addedge(u,v,val);
	}
	cout<<maxflow(s,t)<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值