网络流是一个博大精深的OI类别

今天浅显地理解了一下网络流,做一下笔记

1.网络:是一个有向图,每个边有一个容量c(x,y),每条边也会有一个可行的流量f(x,y),这个f被称为流函数

图中有一个源点S,不断往外流水,只流不入,汇点T则相反。其他的点流入的量等于流出的量。

流/增广路:一条从S 出发,能到达T且经过的边的流函数最小值>0的路径,称为一个流/增广路

网络流:顾名思义,和水流类似,就是在一张网络上流水,每条边好似一个管道。

2.三大定律:

①容量限制:f(x,y)<=c(x,y) 每条边不能超过边的容量。这限制了网络一定有一个最大的总流量

②斜对称:f(x,y)=-f(y,x) 一条边相当于是一个正边和反边组成,从x往y流flow,相当于从y往回流-flow

这一点其实是人为定义的,原图中一般也不画出,但是相当关键,也是可以用dinic,EK等算法直接求最大流的“反悔回流”的条件

这个反边是一定要建的。不要忘了。

③流量守恒。除了源点,汇点之外,每个点流入的总流量,一定等于流出的总流量。

这一点也是很重要的基础,为最大流求法和模型构建提供了成立的条件。

 

之后边的容量保留的是边的剩余流量,正向减去,反向会加上。初始正向是最大容量,反向是0

最大流

之前已经说了,每个点有一个流入的和流出的量。

定义,一个网络的总流量为:∑f(s,v)即从源点流出的总量。

满足三大定律的流函数有很多,其中最多的总流量称为这个网络的最大流。

网络流的扩展变形都是建立在最大流基础上的。

 

算法:

①Edmonds-Karp(EK)利用bfs每次找到一条增广路增广。

(留坑,学完费用流再补)

upda:

EK每次bfs找到一条 增广路,然后对这条增广路进行流量改变。

记录一个incf[x],表示,进入x的流量。

要记录一个pre[x],表示点x是通过哪条边转移过来的。

 

 

一般能处理10^3~10^4的网络

②Dinic算法

发现EK每次最多只找到一条增广路,效率不是很高

Dinic bfs搜出分层图,dfs多路增广,效率就很高了。

放链接:Dinic算法(研究总结,网络流)

一般能处理10^4~10^5的网络。

 

模板:

luoguP3376 【模板】网络最大流

#include<bits/stdc++.h>
using namespace std;
const int N=10000+7;
const int M=100000+7;
const int inf=0x3f3f3f3f;
int n,m;
struct node{
    int nxt,to;
    int w;
}e[2*M];
int hd[N],cnt=1;//从2开始编号,i^1就是反边编号 
int s,t;
void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].w=z;
    hd[x]=cnt;
}
int d[N];
queue<int>q;
bool bfs(){//bfs找分层图 
    while(!q.empty()) q.pop();
    memset(d,0,sizeof d);
    d[s]=1;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(!d[y]&&e[i].w){
                d[y]=d[x]+1;
                q.push(y);
                if(y==t) return 1;
            }
        }
    }
    return 0;
}
int dfs(int x,int flow){//dfs多路增广 
    if(x==t) return flow;
    int rest=flow;
    for(int i=hd[x];i&&rest;i=e[i].nxt){
        int y=e[i].to;
        if(d[y]==d[x]+1&&e[i].w){
            int k=dfs(y,min(rest,e[i].w));
            if(!k) d[y]=0;
            rest-=k;
            e[i].w-=k;
            e[i^1].w+=k;
        }
    }
    return flow-rest;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int x,y,z;
    for(int i=1;i<=m;i++){//建边 
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,0);//反边起初容量是0 
    }
    int maxflow=0;
    int flow;
    while(bfs()) 
      while(flow=dfs(s,inf)) maxflow+=flow;
    printf("%d",maxflow);
    return 0;
}
最大流

相关文章: