zoukankan      html  css  js  c++  java
  • 网络流

    相关资料

    用最通俗的语言让你学会网络流

    EK不够快?再学个Dinic吧

    究级的最大流算法:ISAP与HLPP

    1. 最大流

    基本概念:用最通俗的语言让你学会网络流

    1.EK(Edmond—Karp)算法

    思路十分暴力,就是建反边以便反悔,不断宽搜找可行流,然后暴力扩展,直到没有可行流为止。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=1e9;
    struct Pre{
    	int v/*vertex*/,e/*edge*/;
    }pre[MAXN];
    int N,M,S,T;
    int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
    bool inq[MAXN];
    inline void add(int x,int y,int z){
    	nxt[top]=head[x];
    	head[x]=top;
    	to[top]=y;
    	val[top]=z;
    	top++; 
    }
    inline bool bfs(){
    	queue<int> que;
    	memset(inq,0,sizeof(inq));
    	memset(pre,0,sizeof(pre));
    	que.push(S);
    	inq[S]=1;
    	while(que.size()){
    		int ii=que.front();
    		que.pop();
    		for(int i=head[ii];i;i=nxt[i]){
    			if(!val[i]||inq[to[i]]){
    				continue;
    			}
    			pre[to[i]].v=ii;
    			pre[to[i]].e=i;
    			if(to[i]==T){
    				return 1;
    			}
    			inq[to[i]]=1;
    			que.push(to[i]);
    		}
    	}
    	return 0;
    }
    inline int EK(){
    	int ans=0;
    	while(bfs()){
    		int minv=INF;
    		for(int i=T;i!=S;i=pre[i].v){
    			minv=min(minv,val[pre[i].e]);
    		}
    		for(int i=T;i!=S;i=pre[i].v){
    			val[pre[i].e]-=minv;
    			val[pre[i].e^1]+=minv;
    		}
    		ans+=minv;
    	}
    	return ans;
    }
    int main(){
    	scanf("%d%d%d%d",&N,&M,&S,&T);
    	for(int i=1;i<=M;i++){
    		int ii,jj,kk;
    		scanf("%d%d%d",&ii,&jj,&kk);
    		add(ii,jj,kk);
    		add(jj,ii,0);
    	}
    	printf("%d",EK());
    	return 0;
    }
    

    2.Dinic算法

    Dinic在EK的基础上又一定的改进,每次宽搜顺便分层,然后用深搜扩展所有可行流,如此往复。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
    int N,M,S,T;
    int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
    int inq[MAXN],de[MAXN];
    inline void add(int x,int y,int z){
        nxt[top]=head[x];
        head[x]=top;
        to[top]=y;
        val[top]=z;
        top++;
    }
    inline bool bfs(){
        queue<int> que;
        memset(inq,0,sizeof(inq));
        memset(de,0x3f,sizeof(de));
        que.push(S);
        de[S]=0;
        while(que.size()){
            int ii=que.front();
            que.pop();
            inq[ii]=0;
            for(int i=head[ii];i;i=nxt[i]){
                if(val[i]&&de[to[i]]>de[ii]+1){
                    de[to[i]]=de[ii]+1;
                    if(!inq[to[i]]){
                        que.push(to[i]);
                        inq[to[i]]=1;
                    }
                }
            }
        }
        return de[T]==INF?0:1;
    }
    int dfs(int x,int flow){
        if(x==T){
            return flow;
        }
        for(int i=head[x];i;i=nxt[i]){
            if(val[i]&&de[to[i]]==de[x]+1){
                int ii=dfs(to[i],min(flow,val[i]));
                if(ii){
                    val[i]-=ii;
                    val[i^1]+=ii;
                    return ii;
                }
            }
        }
        return 0;
    }
    inline int Dinic(){
        int ans=0,ii;
        while(bfs()){
            do{
                ii=dfs(S,INF);
                ans+=ii;
            }while(ii);
        }
        return ans;
    }
    int main(){
        scanf("%d%d%d%d",&N,&M,&S,&T);
        for(int i=1;i<=M;i++){
            int ii,jj,kk;
            scanf("%d%d%d",&ii,&jj,&kk);
            add(ii,jj,kk);
            add(jj,ii,0);
        }
        printf("%d",Dinic());
        return 0;
    }
    

    另外,在Dinic的基础下,还可以进行当前弧优化,也就是说在同一次宽搜后,每一条边只访问一次,可以提升一定的效率

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
    int N,M,S,T;
    int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
    int inq[MAXN],de[MAXN],cur[MAXN];
    inline void add(int x,int y,int z){
    	nxt[top]=head[x];
    	head[x]=top;
    	to[top]=y;
    	val[top]=z;
    	top++;
    }
    inline bool bfs(){
    	queue<int> que;
    	memset(inq,0,sizeof(inq));
    	memset(de,0x3f,sizeof(de));
    	memcpy(cur,head,sizeof(cur));
    	que.push(S);
    	de[S]=0;
    	while(que.size()){
    		int ii=que.front();
    		que.pop();
    		inq[ii]=0;
    		for(int i=head[ii];i;i=nxt[i]){
    			if(val[i]&&de[to[i]]>de[ii]+1){
    				de[to[i]]=de[ii]+1;
    				if(!inq[to[i]]){
    					que.push(to[i]);
    					inq[to[i]]=1;
    				}
    			}
    		}
    	}
    	return de[T]==INF?0:1;
    }
    int dfs(int x,int flow){
    	if(x==T){
    		return flow;
    	}
    	for(int i=cur[x];i;i=nxt[i]){
    		cur[x]=i;
    		if(val[i]&&de[to[i]]==de[x]+1){
    			int ii=dfs(to[i],min(flow,val[i]));
    			if(ii){
    				val[i]-=ii;
    				val[i^1]+=ii;
    				return ii;
    			}
    		}
    	}
    	return 0;
    }
    inline int Dinic(){
    	int ans=0,ii;
    	while(bfs()){
    		do{
    			ii=dfs(S,INF);
    			ans+=ii;
    		}while(ii);
    	}
    	return ans;
    }
    int main(){
    	scanf("%d%d%d%d",&N,&M,&S,&T);
    	for(int i=1;i<=M;i++){
    		int ii,jj,kk;
    		scanf("%d%d%d",&ii,&jj,&kk);
    		add(ii,jj,kk);
    		add(jj,ii,0);
    	}
    	printf("%d",Dinic());
    	return 0;
    }
    

    Dinic还可以加used优化,记录已经用过的总流量,防止找到过多流量。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
    int N,M,S,T,maxflow;
    int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
    int inq[MAXN],de[MAXN],cur[MAXN];
    inline void add(int x,int y,int z){
    	nxt[top]=head[x];
    	head[x]=top;
    	to[top]=y;
    	val[top]=z;
    	top++;
    }
    inline bool bfs(){
    	queue<int> que;
    	memset(inq,0,sizeof(inq));
    	memset(de,0x3f,sizeof(de));
    	memcpy(cur,head,sizeof(cur));
    	que.push(S);
    	de[S]=0;
    	while(que.size()){
    		int ii=que.front();
    		que.pop();
    		inq[ii]=0;
    		for(int i=head[ii];i;i=nxt[i]){
    			if(val[i]&&de[to[i]]>de[ii]+1){
    				de[to[i]]=de[ii]+1;
    				if(!inq[to[i]]){
    					que.push(to[i]);
    					inq[to[i]]=1;
    				}
    			}
    		}
    	}
    	return de[T]==INF?0:1;
    }
    int dfs(int x,int flow){//·µ»ØÒÑÓÃÁ÷Á¿ 
    	if(x==T){
    		maxflow+=flow;
    		return flow;
    	}
    	int used=0;
    	for(int i=cur[x];i;i=nxt[i]){
    		cur[x]=i;
    		if(val[i]&&de[to[i]]==de[x]+1){
    			int ii=dfs(to[i],min(flow-used,val[i]));//·ÀÖ¹³¬¹ýÁ÷Á¿ 
    			if(ii){
    				used+=ii;
    				val[i]-=ii;
    				val[i^1]+=ii;
    				if(used==flow){//ÂúÁ÷Á¿ 
    					break;
    				}
    			}
    		}
    	}
    	return used;
    }
    inline void Dinic(){
    	while(bfs()){
    		dfs(S,INF);
    	}
    	printf("%d",maxflow);
    }
    int main(){
    	scanf("%d%d%d%d",&N,&M,&S,&T);
    	for(int i=1;i<=M;i++){
    		int ii,jj,kk;
    		scanf("%d%d%d",&ii,&jj,&kk);
    		add(ii,jj,kk);
    		add(jj,ii,0);
    	}
    	Dinic();
    	return 0;
    }
    

    3. ISAP算法

    ISAP在Dinic基础上优化,只做一次bfs,从T向S统计深度,当一个点的出流量大于入流量时将他的深度加一,出现断层时就结束。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
    int N,M,S,T,maxflow;
    int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
    int gap[MAXN],de[MAXN],cur[MAXN];
    inline void add(int x,int y,int z){
    	nxt[top]=head[x];
    	head[x]=top;
    	to[top]=y;
    	val[top]=z;
    	top++;
    }
    inline void bfs(){
    	queue<int> que;
    	memset(gap,0,sizeof(gap));
    	memset(de,-1,sizeof(de));
    	que.push(T);
    	de[T]=0;
    	gap[0]=1;
    	while(que.size()){
    		int ii=que.front();
    		que.pop();
    		for(int i=head[ii];i;i=nxt[i]){
    			if(de[to[i]]!=-1){
    				continue;
    			}
    			de[to[i]]=de[ii]+1;
    			gap[de[to[i]]]++;
    			que.push(to[i]);
    		}
    	}
    }
    int dfs(int x,int flow){
    	if(x==T){
    		maxflow+=flow;
    		return flow;
    	}
    	int used=0;
    	for(int i=cur[x];i;i=nxt[i]){
    		cur[x]=i;
    		if(val[i]&&de[to[i]]+1==de[x]){
    			int ii=dfs(to[i],min(flow-used,val[i]));//·ÀÖ¹³¬¹ýÁ÷Á¿ 
    			if(ii){
    				used+=ii;
    				val[i]-=ii;
    				val[i^1]+=ii;
    				if(used==flow){
    					return used;
    				}
    			}
    		}
    	}
    	gap[de[x]]--;
    	if(!gap[de[x]]){
    		de[S]=N+1;
    	}
    	de[x]++;
    	gap[de[x]]++;
    	return used;
    }
    inline void ISAP(){
    	bfs();
    	while(de[S]<N){
    		memcpy(cur,head,sizeof(cur));
    		dfs(S,INF);
    	}
    	printf("%d",maxflow);
    }
    int main(){
    	scanf("%d%d%d%d",&N,&M,&S,&T);
    	for(int i=1;i<=M;i++){
    		int ii,jj,kk;
    		scanf("%d%d%d",&ii,&jj,&kk);
    		add(ii,jj,kk);
    		add(jj,ii,0);
    	}
    	ISAP();
    	return 0;
    }
    

    2.最小费用最大流

    1. EK算法

    将bfs改成spfa求最短路即可

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
    struct Pre{
    	int e,v;
    }pre[MAXN];
    int N,M,S,T;
    int head[MAXN],to[MAXM*2],val[MAXM*2],cost[MAXM*2],nxt[MAXM*2],tp=2;
    int dis[MAXN];
    bool inq[MAXN];
    inline void add(int x,int y,int z,int k){
    	nxt[tp]=head[x];
    	head[x]=tp;
    	to[tp]=y;
    	val[tp]=z;
    	cost[tp]=k;
    	tp++;
    }
    inline bool spfa(){
    	queue<int> que;
    	memset(pre,0,sizeof(pre));
    	memset(dis,0x3f,sizeof(dis));
    	memset(inq,0,sizeof(inq));
    	inq[S]=1;
    	dis[S]=0;
    	que.push(S);
    	while(que.size()){
    		int ii=que.front();
    		que.pop();
    		inq[ii]=0;
    		for(int i=head[ii];i;i=nxt[i]){
    			if(val[i]&&dis[to[i]]>dis[ii]+cost[i]){
    				dis[to[i]]=dis[ii]+cost[i];
    				pre[to[i]].v=ii;
    				pre[to[i]].e=i;
    				if(!inq[to[i]]){
    					inq[to[i]]=1;
    					que.push(to[i]);
    				}
    			}
    		}
    	}
    	return dis[T]==INF?0:1;
    }
    inline void EK(){
    	int ans=0,tot=0;
    	while(spfa()){
    		int minv=INF;
    		for(int i=T;i!=S;i=pre[i].v){
    			minv=min(minv,val[pre[i].e]);
    		}
    		for(int i=T;i!=S;i=pre[i].v){
    			val[pre[i].e]-=minv;
    			val[pre[i].e^1]+=minv;
    		}
    		ans+=minv;
    		tot+=minv*dis[T];
    	}
    	printf("%d %d",ans,tot);
    }
    int main(){
    	scanf("%d%d%d%d",&N,&M,&S,&T);
    	for(int i=1;i<=M;i++){
    		int ii,jj,kk,ll;
    		scanf("%d%d%d%d",&ii,&jj,&kk,&ll);
    		add(ii,jj,kk,ll);
    		add(jj,ii,0,-ll);
    	}
    	EK();
    	return 0;
    }
    

    2. Dinic

    和Dinic一样,但不可以当前弧优化

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
    int N,M,S,T,maxflow,totcost;
    int head[MAXN],to[MAXM*2],val[MAXM*2],cost[MAXM*2],nxt[MAXM*2],tp=2;
    int vis[MAXN],dis[MAXN];
    inline void add(int x,int y,int z,int k){
    	nxt[tp]=head[x];
    	head[x]=tp;
    	to[tp]=y;
    	val[tp]=z;
    	cost[tp]=k;
    	tp++;
    }
    inline bool spfa(){
    	queue<int> que;
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f,sizeof(dis));
    	que.push(S);
    	vis[S]=1;
    	dis[S]=0;
    	while(que.size()){
    		int ii=que.front();
    		que.pop();
    		vis[ii]=0;
    		for(int i=head[ii];i;i=nxt[i]){
    			if(val[i]&&dis[to[i]]>dis[ii]+cost[i]){
    				dis[to[i]]=dis[ii]+cost[i];
    				if(!vis[to[i]]){
    					que.push(to[i]);
    					vis[to[i]]=1;
    				}
    			}
    		}
    	}
    	return dis[T]==INF?0:1;
    }
    int dfs(int x,int flow){
    	if(x==T){
    		maxflow+=flow;
    		return flow;
    	}
    	int used=0;
    	vis[x]=1;
    	for(int i=head[x];i;i=nxt[i]){
    		if(!vis[to[i]]&&val[i]&&dis[to[i]]==dis[x]+cost[i]){
    			int ii=dfs(to[i],min(flow-used,val[i]));
    			if(ii){
    				totcost+=ii*cost[i];
    				used+=ii;
    				val[i]-=ii;
    				val[i^1]+=ii;
    				if(used==flow){
    					break;
    				}
    			}
    		}
    	}
    	return used;
    }
    inline void Dinic(){
    	while(spfa()){
    		dfs(S,INF);
    	}
    	printf("%d %d",maxflow,totcost);
    }
    int main(){
    	scanf("%d%d%d%d",&N,&M,&S,&T);
    	for(int i=1;i<=M;i++){
    		int ii,jj,kk,ll;
    		scanf("%d%d%d%d",&ii,&jj,&kk,&ll);
    		add(ii,jj,kk,ll);
    		add(jj,ii,0,-ll);
    	}
    	Dinic();
    	return 0;
    }
    
  • 相关阅读:
    我受不了了,可能拿不到毕业证了
    [My B.S paper draft]我的本科答辩论文草稿
    Memory Dog for Autodesk Maya
    Silent Hill 5 Bug
    AMPAS/ASC Common File Format LUT
    CUDAAccelerated LUT Converter for DI Workflow
    Forking AfterBurn into Maya
    nicEdit上传图片无法显示的问题
    用插值的方法进行直方图平滑
    一个新的做presentation的利器
  • 原文地址:https://www.cnblogs.com/guoshaoyang/p/11120972.html
Copyright © 2011-2022 走看看