• 神仙网络最大流


    好久没有写博客的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;
    }
    
  • 相关阅读:
    关于回溯与招聘市场
    关于回溯与马
    关于回溯和后宫
    关于兔子
    关于递归和斐波那契数列
    关于递归和汉诺塔
    关于简单汉诺塔
    nodejs报错roll back,because of a error.node.js setup wizard ended prematurel
    fatal error C1859 意外的预编译头错误,只需重新运行编译器
    sqlserver2008 无法设置主体sa的凭据
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11358240.html
Copyright © 2020-2023  润新知