参考题目:loj101
解析:
先介绍几个概念(公式恐惧症慎入)。
流网络与流
流网络 G = ( V , E ) G=(V,E) G=(V,E)是一个有向图。并且对于 ∀ e ∈ E \forall e \in E ∀e∈E,边 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
<
=
f
[
e
]
<
=
c
a
p
[
e
]
0<=f[e]<=cap[e]
0<=f[e]<=cap[e]
流量守恒:对于
∀
u
∈
V
−
{
s
,
t
}
\forall u\in V-\{s,t\}
∀u∈V−{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)
v∈V∑f(u,v)=v∈V∑f(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∣=(v∈V)∑∣f(s,v)∣−v∈V∑∣f(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 Ford−Fullkerson和 E d m o n d s − K a r p Edmonds-Karp Edmonds−Karp算法。但是效率不是很高。
先介绍一下一般的最大流算法。
首先明确两个概念。
残存网络和增广路
在已经找出一个流之后,定义边的残存容量为 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') Gf=(V,E′),其中 E ′ = { e ′ ∣ e ∈ E , e ′ = e = ( u , v ) , c ( e ′ ) = c f ( e ) } E'=\{e'|e\in E,e'=e=(u,v),c(e')=c_f(e)\} E′={e′∣e∈E,e′=e=(u,v),c(e′)=cf(e)}(其中包括原图的反向边)。我们一般做网络流问题都是直接在残存网络上做。
如果我们找出一条路径 s − > t s->t s−>t,并且路径上的剩余容量最小的边的容量大于0,那么我们就称 s − > t s->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;
}