介绍

网络流是有向图,边权是边的容量,形象的理解就是水管单位时间的流水量
源点\((S)\):水源,可以提供无穷多的水
汇点\((T)\):水的汇集点

学习笔记——网络流_最小割

对于每条边:它的流量小于它的容量,且流量不能为负

同时,除了汇点与源点的其他节点,流入总量=流出总量

汇点流入总量=最大流

最大流

顾名思义,最大流就是流入汇点总量最大。

不断地找增广路,即找到一条路,路上每条边的流量都小于容量。找到一条路后,修改每条路上的流量,这条路的流量=路上的边\(min(容量-流量)\)

学习笔记——网络流_最大流_02

上图中第一次找路,找到\(1->2->3->4\),找到后,修改每条边上的容量,就再也找不到路了。

但如果边的流量可以回退,下次找到\(1->3->2->4\)才能算出真正的最大流。

所以网络流的存图:每条边都可以后悔,即每条边存正反两条边。

一开始:正边容量=原来容量,反边容量=0

修改流量:正边容量-=路容量,反边容量+=路容量

在写程序时,为了快速知道一条边的反边,我们让正反边边号满足:反边=正边 XOR 1

找增广路的算法:isap

isap有个\(dis\)数组,增广时沿着\(dis\)数组连续减一的路线走,isap不依靠\(bfs\)来修改\(dis\)数组,在\(dfs\)增广时,如果找不到增广路了,就修改\(dis\)数组:\(dis[x]++\)

虽然\(isap\)不用\(bfs\),但为了减少常数,最好事先\(bfs\)一次,预处理出初始的\(dis\)数组

非常重要的优化:GAP优化

在增广的时候,随时可能\(dis[x]++\),可能某个时候,全图中已经没有\(dis[y]=dis[x]\)的节点\(y\)了,此时,\(dis<x\)\(dis>x\),该图断层了,说明增广路已经找完,可以返回了,因此记录\(dis\)数组出现次数,如果为\(0\),则标记\(dis[S]=n\)表示算法结束。

\(code:\)

int S,T,Last[MAX_V],Next[MAX_E<<1],End[MAX_E<<1],len[MAX_E<<1],tot=1;
inline void addedge(int x,int y,int z){//建网络流正反边
	End[++tot]=y,Next[tot]=Last[x],Last[x]=tot,len[tot]=z;
	End[++tot]=x,Next[tot]=Last[y],Last[y]=tot,len[tot]=0;
}
int dis[MAX_V],gap[MAX_V];
queue<int> q;
void bfs(){
    for(int i=1;i<=T;i++) dis[i]=T;//初始化dis数组
    dis[T]=0;
    queue<int> q;
    q.push(T);//倒着进行bfs
    while(q.size()){
        int x=q.front();
        q.pop();
        for(int i=Last[x];i;i=Next[i]){
            int y=End[i];
            if(dis[y]>dis[x]+1){//更新
                dis[y]=dis[x]+1;
                q.push(y);
            }
        }
    }
    for(int i=1;i<=T;i++) gap[dis[i]]++;//记录dis数组次数
    return;
}
int isap(int x,int flow){
    if(x==T) return flow;//到了源点
    int flow_now=0;
    for(int i=_last[x];i;i=Next[i]){
        int y=End[i];
        if(len[i] && dis[x]==dis[y]+1){
            int f=isap(y,min(len[i],flow-flow_now));
            flow_now+=f;
            len[i]-=f;//正边-=f
            len[i^1]+=f;//反边+=f
            if(flow==flow_now || dis[S]==T) return flow_now;
        }
    }
    gap[dis[x]]--;
    if(!gap[dis[x]]) dis[S]=T;//断层
    dis[x]++;
    gap[dis[x]]++;
    return flow_now;
}

最小割

割集:选一些边,如果删掉这些边,源点和汇点就无法互相到达了,则这些边所组成的集合就是割集

最小割:容量总和最小的割集

在网络流上,最小割=最大流

其实,最小割可以看成是一刀将图的元素分割成了两个集合,一些属于源点,一些属于汇点,所以通常最小割的题目需要将题目信息给分开成两个不同集合。

注意:最小割的题,一定要将题目信息转化成“xxx的总和的最小值”,才能最小割建图。