神仙网络最大流

好久没有写博客的lz的突然一篇博客,马上要开学了,怕是上机时间会少很多。

(本篇引用多次百度百科因为实在不知道怎么用语言描述)

有关网络流-最大流:

首先啥是网络流-最大流:

网络流,是基于有向图的;

首先有一个源点,一个汇点:

然后在源点和汇点的基础上,我们衍生出许多路径。

每条边都有一个最大流量,对于一条路径来说,这条路径的最大流量就是这条路径上最小的最大流量的值。对于分支情况,我们也可以将当前水流分别流向几个地方;

emmm挺乱的,我们看个例子?

反正大概就这个亚子。

然后我们可以把源点看做是有无限多的流量,求最大流就是求从源点最大可以流到汇点的流量是多少,上图的最大流就是9;

然后显然要考虑怎么求最大流。

引入增广路的概念:

增广路是指从S到T的一条路,流过这条路,使得当前的流量可以增加。(摘自

EK算法:

就是不断地寻找增广路,将流量加入汇点,直至找不到增广路,此时汇点的流量就是最大流。

算法流程:

从S到T广搜,从S开始不断向外广搜,通过权值大于 0的边(因为后面会减边权值,所以可能存在边权为0的边),直到找到T为止,然后找到该路径上边权最小的边,记为\(minf\),然后最大流加\(minf\) ,然后把该路径上的每一条边的边权减去\(minf\),把该路径上的每一条边的反向边的边权加上 \(minf\) ,直到找不到一条增广路为止。

时间复杂度\(O(nm^2)\),可以处理\(10^3\)~\(10^4\)规模的网络。

然后是模板题

#include<bits/stdc++.h>
#define inf 1000000007//然后因为把define放在这里被water_lift嫌弃毒瘤rwr

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}
bitset<10010> vis;
int n,m,s,t;
int ecnt=1,ans;
int head[10010];
struct node{
	int to,dis,nxt;
}e[200010];
void add(int from,int to,int dis){
    ++ecnt;
    e[ecnt].to=to;
    e[ecnt].dis=dis;
    e[ecnt].nxt=head[from];
    head[from]=ecnt;
}

int now[10010],pre[10010];

bool bfs(){
    vis.reset();
    vis[s]=1;
    queue<int> q;
    q.push(s);
    now[s]=inf;
    while(!q.empty()){
		int u=q.front();
        q.pop();
        for(int i=head[u],v,w;i;i=e[i].nxt){
        	
            v=e[i].to;w=e[i].dis;
            if(!w||vis[v]) continue;
            now[v]=min(now[u],w);
            pre[v]=i;
			if(v==t) return 1;
            q.push(v);
            vis[v]=1;
        }
    }
    return 0;
}

void update(){
    ans+=now[t];
    int x=t;
    while(x!=s){
    	int i=pre[x];
        e[i].dis-=now[t];
        e[i^1].dis+=now[t];
        x=e[i^1].to;
    }
}

int main(){
    n=read();m=read();
    s=read();t=read();
    int u,v,w;
    for(int i=1;i<=m;i++){
        u=read();
        v=read();
        w=read();
        add(u,v,w);
        add(v,u,0);
    }
    while(bfs())update();
    printf("%d",ans);
}

然后基本上长得一模一样的例题

Luogu P2740 [USACO4.2]草地排水Drainage Ditches

Dinic算法

(我是懵的rwr)

  • 残量网络

在任意时刻,网络中所有节点以及剩余容量大于0的边构成的子图被称为残量网络。EK算法每轮可能会遍历整个残量网络,但只找出1条增广路。

  • 分层图

节点层次 \(d_x\)表示 S 到 x 最少需要经过的边数。在残量网络中,满足 \(d_y = d_x + 1\)的边 (x,y) 构成的子图被称为分层图。显然,分层图是一张有向无环图

  • 算法步骤

不断重复以下步骤,直到残量网络中S不能到达T:

  1. 在残量网络上 BFS 求出节点层次,构造分层图。
  2. 在分层图上 DFS 寻找增广路,在回溯时实时更新剩余容量。另外,每个点可以流向多条出边,同时还加入若干剪枝。

时间复杂度

\(O(n^2m)\),实际运用中远远达不到,能够处理 \(10^4\) ~ \(10^5\)规模的网络。

上面模板题的dinic做法:

#include<bits/stdc++.h>

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}
const int mxn=10010;
const int mxm=100010;
const int inf=2147483647;

int n,m,s,t;
struct node{
    int to,dis,nxt;
}e[mxm<<1];
int ecnt=1,head[mxn],dep[mxn],cur[mxn];

void add(int from,int to,int dis){
    ecnt++;
    e[ecnt].to=to;
    e[ecnt].dis=dis;
    e[ecnt].nxt=head[from];
    head[from]=ecnt;
}

bool bfs(){
    queue<int> q;
    while(!q.empty()) q.pop();
    memset(dep,0,sizeof(dep));
    q.push(s);
    dep[s]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u],v;i;i=e[i].nxt){
            v=e[i].to;
            if(e[i].dis>0&&!dep[v]){
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    if(dep[t]==0) return 0;
    return 1;
}

int dfs(int u,int dis){
    if(u==t) return dis;
    for(int &i=cur[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].dis!=0){
            int k=dfs(v,min(dis,e[i].dis));
        	if(k>0){
            	e[i].dis-=k;
                e[i^1].dis+=k;
                return k;
        	}
        }
    }
    return 0;
}

int main(){
	n=read();m=read();
    s=read();t=read();
    int u,v,w;
    for(int i=1;i<=m;i++){
        u=read();v=read();w=read();
        add(u,v,w);add(v,u,0);
    }
    int d,ans=0;
    while(bfs()){
        for(int i=1;i<=n;i++)cur[i]=head[i];
        while(d=dfs(s,inf)) ans+=d;
    }
    printf("%d",ans);
    return 0;
}

对于当前弧优化,我是懵的,只能说背背板子rwr;

如何用网络流解决二分图匹配问题:

首先我们需要建立一个超级源点S,还要建立一个超级汇点T;

然后我们将所有在左边的点和源点S连一条长度为1的边,将所有在右边的点和汇点T连一条长度为1的边,对于中间的边,我们按照输入加入长度为1的边,然后跑一边由S到T的网络最大流就可以得到结果啦。

Luogu 【模板】二分图匹配

//网络流做法:
#include<bits/stdc++.h>

using namespace std;

inline int read(){
	int ans=0;
	char last=' ',ch=getchar();
	while(ch>'9'||ch<'0') last=ch,ch=getchar();
	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
	if(last=='-') ans=-ans;
	return ans;
}
const int mxn=10010;
const int mxm=1000010;
const int inf=2147483647;

int n,m,ee;
int s,t;
struct node{
    int to,dis,nxt;
}e[mxm<<1];
int ecnt=1,head[mxn],dep[mxn],cur[mxn];

void add(int from,int to,int dis){
    ecnt++;
    e[ecnt].to=to;
    e[ecnt].dis=dis;
    e[ecnt].nxt=head[from];
    head[from]=ecnt;
}

bool bfs(){
    queue<int> q;
    while(!q.empty()) q.pop();
    memset(dep,0,sizeof(dep));
    q.push(s);
    dep[s]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u],v;i;i=e[i].nxt){
            v=e[i].to;
            if(e[i].dis>0&&!dep[v]){
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    if(dep[t]==0) return 0;
    return 1;
}

int dfs(int u,int dis){
    if(u==t) return dis;
    for(int &i=cur[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(dep[v]==dep[u]+1&&e[i].dis!=0){
            int k=dfs(v,min(dis,e[i].dis));
        	if(k>0){
            	e[i].dis-=k;
                e[i^1].dis+=k;
                return k;
        	}
        }
    }
    return 0;
}

int main(){
	n=read();m=read();
	ee=read();
	int u,v;
	int nn=n+m+2;
	for(int i=1;i<=n;i++){
		add(1,i+1,1);
		add(i+1,1,0);
	}
	for(int i=1;i<=ee;i++){
		u=read();v=read();
		if(u<=n&&v<=m){
			add(u+1,v+n+1,1);
			add(v+n+1,u+1,0);
		}
	}
	for(int i=1;i<=m;i++){
		add(i+n+1,nn,1);
		add(nn,i+n+1,0);
	}
	s=1;t=nn;
	 int d,ans=0;
    while(bfs()){
        for(int i=1;i<=nn;i++)cur[i]=head[i];
        while(d=dfs(s,inf)) ans+=d;
    }
    printf("%d",ans);
	return 0;
}
//匈牙利算法:
#include<bits/stdc++.h>

using namespace std;

inline int read(){
	int ans=0;
	char last=' ',ch=getchar();
	while(ch>'9'||ch<'0') last=ch,ch=getchar();
	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
	if(last=='-') ans=-ans;
	return ans;
}
const int mxn=1010;
const int mxm=1010;

int n,m,e;

int girl[mxm],f[mxn][mxm],vis[mxm];

bool work(int u){
	for(int i=1;i<=m;i++){
		if(!vis[i]&&f[u][i]){
			vis[i]=1;
			if(!girl[i]||work(girl[i])){
				girl[i]=u;
				return 1;
			}
		}
	}
	return 0;
}
int ans;

int main(){
	n=read();m=read();e=read();
	int u,v;
	for(int i=1;i<=e;i++){
		u=read();
		v=read();
		if(u>n||v>m) continue;
		f[u][v]=1;
	}
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		ans+=work(i);
	}
	printf("%d",ans);
	return 0;
}
posted @ 2019-08-15 15:29  Sweetness  阅读(244)  评论(3编辑  收藏  举报