zoukankan      html  css  js  c++  java
  • 有上下界网络流 学习笔记

    一、说在前头

    前置知识:(dinic)

    普通的网络流,我们只限制了每条路的流量,也就是它的上界,但有些特殊题目,还会恶心地限制一下每条道路流量的下界,今天我们要解决的就是这样的题目。

    二、无源汇有上下界可行流

    这个算法是所有上下界网络流算法的基础,因此我会重点阐述。首先解释一下标题:可行流是指一个网络流中存在的一条满足除源点汇点之外,所有点的流入量等于流出量(即流量守恒)的流。无源汇,就表明这个图中所有点都必须满足流入量等于流出量,我们只需要找到任意一条可行流即可。

    首先,我们先让所有边流量强行达到下界,这样的一个流我们称为初始流,显然,初始流不一定满足流量守恒,所以我们需要通过适当在一些边上增加流量来使它满足流量守恒,新增加的流量合起来也是一条流,我们称为附加流。

    附加流首先要满足不会让任意一条边流量超过上界,因此,我们将每条边的最大流量设为上界-下界,同时还要弥补初始流中的问题。对于点(i),令(w_i)表示初始流中(i)的流入量-流出量,我们对(w_i)分情况讨论:

    • (w_i=0),初始流流量守恒,不需要对(i)点操作,让附加流满足流量守恒即可。
    • (w_i>0),附加流在满足(i)点流量守恒之后,还要让它多流出(w_i)的流量,于是从附加流的源点(S)(i)连一条流量为(w_i)的边。
    • (w_i<0),附加流要让(i)点额外流入(-w_i)的流量,于是从(i)向附加流的汇点(T)连一条流量为(-w_i)的边。

    为了满足流量守恒,额外连的这些边必须满流,于是在新图上跑最大流,只要流量(=sum{w_i}(w_i>0)=sum{-w_i}(w_i<0))则说明有解,否则无解。注意到第二个等号一定满足,所以只用考虑第一个等号即可。

    最后求可行流,则将附加流中每条原图中边的流量加上其下界即可。

    模板:loj#115

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=410;
    const int M=4e5+10;
    const int inf=0x3f3f3f3f;
    namespace flow{
    	struct node{
    		int v,f,nxt;
    	}e[M];
    	int tot;
    	int s,t,cnt=1,cur[N],first[N],vis[N],dis[N];
    	inline void add(int u,int v,int f){
    		e[++cnt].v=v;e[cnt].f=f;e[cnt].nxt=first[u];first[u]=cnt;
    	}
    	inline void Add(int u,int v,int f){
    		add(u,v,f);add(v,u,0);
    	}
    	inline bool bfs(){
    		fill(vis+1,vis+tot+1,0);
    		fill(dis+1,dis+tot+1,0);
    		queue<int> q;q.push(s);
    		dis[s]=0;vis[s]=1;
    		while(!q.empty()){
    			int u=q.front();q.pop();
    			for(int i=first[u];i;i=e[i].nxt){
    				int v=e[i].v;
    				if(e[i].f>0&&!vis[v]){
    					vis[v]=1;dis[v]=dis[u]+1;
    					q.push(v);
    				}
    			}
    		}
    		return vis[t];
    	}
    	inline int dfs(int u,int f){
    		if(u==t||!f) return f;
    		int used=0;
    		for(int &i=cur[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(e[i].f>0&&dis[v]==dis[u]+1){
    				int fl=dfs(v,min(f,e[i].f));
    				used+=fl;f-=fl;
    				e[i].f-=fl;e[i^1].f+=fl;
    				if(!f) break;
    			}
    		}
    		if(f) dis[u]=inf;
    		return used;
    	}
    	inline int dinic(int S,int T){
    		int ans=0;s=S;t=T;
    		while(bfs()){
    			memcpy(cur,first,sizeof(first));
    			ans+=dfs(s,inf);
    		}
    		return ans;
    	}
    	inline void init(){
    		fill(first+1,first+tot+1,0);tot=0;
    		cnt=1;
    	}
    }
    using flow::tot;
    using flow::Add;
    using flow::cnt;
    int n,m,s,t,S,T,w[N],pos[N],L[M];
    int main(){
    	scanf("%d%d",&n,&m);
    	tot=n;S=++tot;T=++tot;
    	for(int i=1;i<=m;++i){
    		int u,v,l,r;
    		scanf("%d%d%d%d",&u,&v,&l,&r);L[i]=l;
    		Add(u,v,r-l);w[u]-=l;w[v]+=l;
    	}
    	int sum=0;
    	for(int i=1;i<=n;++i){
    		if(w[i]>0) sum+=w[i],Add(S,i,w[i]);
    		if(w[i]<0) Add(i,T,-w[i]);
    	}
    	if(flow::dinic(S,T)!=sum){
    		puts("NO");
    		return 0;
    	}
    	else{
    		puts("YES");
    		for(int i=1,j=3;i<=m;++i,j+=2)
    			printf("%d
    ",L[i]+flow::e[j].f);
    	}
    	return 0;
    }
    

    三、有源汇有上下界可行流

    增加了源点(s)和汇点(t)(注意与上文中的(S)(T)区分),源点和汇点不需要满足流量守恒,源点可以任意流出,汇点可以任意流入。

    于是我们直接增加一条从(t)连向(s)的流量上限为(inf),无下限的边,然后按照无源汇的做即可,同时这种情况下,可行流对应的原图流量,可以直接通过(t ightarrow s)这条边的流量得到。

    四、有源汇上下界最大流

    在可行流的基础上,要求原图流量最大。

    我们先根据上一个问题的方法,求解出一条可行流。此时我们一定满足了流量守恒,那么在附加流的残量网络上,我们任意跑出一条可行流,与之前的流合并都一定是一条原图上的可行流,于是直接在残量网络上跑(s ightarrow t)的最大流,与之前的可行流合并即可。注意跑最大流的时候要把那条增加的(t ightarrow s)的边流量给清零。

    模板:loj#116

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=4e4+10;
    const int M=4e5+10;
    const int inf=0x3f3f3f3f;
    namespace flow{
    	struct node{
    		int v,f,nxt;
    	}e[M];
    	int tot;
    	int s,t,cnt=1,cur[N],first[N],vis[N],dis[N];
    	inline void add(int u,int v,int f){
    		e[++cnt].v=v;e[cnt].f=f;e[cnt].nxt=first[u];first[u]=cnt;
    	}
    	inline void Add(int u,int v,int f){
    		add(u,v,f);add(v,u,0);
    	}
    	inline bool bfs(){
    		fill(vis+1,vis+tot+1,0);
    		fill(dis+1,dis+tot+1,0);
    		queue<int> q;q.push(s);
    		dis[s]=0;vis[s]=1;
    		while(!q.empty()){
    			int u=q.front();q.pop();
    			for(int i=first[u];i;i=e[i].nxt){
    				int v=e[i].v;
    				if(e[i].f>0&&!vis[v]){
    					vis[v]=1;dis[v]=dis[u]+1;
    					q.push(v);
    				}
    			}
    		}
    		return vis[t];
    	}
    	inline int dfs(int u,int f){
    		if(u==t||!f) return f;
    		int used=0;
    		for(int &i=cur[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(e[i].f>0&&dis[v]==dis[u]+1){
    				int fl=dfs(v,min(f,e[i].f));
    				used+=fl;f-=fl;
    				e[i].f-=fl;e[i^1].f+=fl;
    				if(!f) break;
    			}
    		}
    		if(f) dis[u]=inf;
    		return used;
    	}
    	inline int dinic(int S,int T,int tp){
    		if(tp) e[2].f=e[3].f=0;
    		int ans=0;s=S;t=T;
    		while(bfs()){
    			memcpy(cur,first,sizeof(first));
    			ans+=dfs(s,inf);
    		}
    		return ans;
    	}
    	inline void init(){
    		fill(first+1,first+tot+1,0);tot=0;
    		cnt=1;
    	}
    }
    using flow::tot;
    using flow::Add;
    using flow::cnt;
    int n,m,s,t,S,T,w[N],pos[N];
    int main(){
    	scanf("%d%d%d%d",&n,&m,&s,&t);
    	tot=n;S=++tot;T=++tot;
    	Add(t,s,inf);
    	for(int i=1;i<=m;++i){
    		int u,v,l,r;
    		scanf("%d%d%d%d",&u,&v,&l,&r);
    		Add(u,v,r-l);w[u]-=l;w[v]+=l;
    	}
    	int sum=0;
    	for(int i=1;i<=n;++i){
    		if(w[i]>0) sum+=w[i],Add(S,i,w[i]);
    		if(w[i]<0) Add(i,T,-w[i]);
    	}
    	if(flow::dinic(S,T,0)!=sum){
    		puts("please go home to sleep");
    		return 0;
    	}
    	else printf("%d
    ",flow::e[3].f+flow::dinic(s,t,1));
    	return 0;
    }#include<bits/stdc++.h>
    using namespace std;
    const int N=4e4+10;
    const int M=4e5+10;
    const int inf=0x3f3f3f3f;
    namespace flow{
    	struct node{
    		int v,f,nxt;
    	}e[M];
    	int tot;
    	int s,t,cnt=1,cur[N],first[N],vis[N],dis[N];
    	inline void add(int u,int v,int f){
    		e[++cnt].v=v;e[cnt].f=f;e[cnt].nxt=first[u];first[u]=cnt;
    	}
    	inline void Add(int u,int v,int f){
    		add(u,v,f);add(v,u,0);
    	}
    	inline bool bfs(){
    		fill(vis+1,vis+tot+1,0);
    		fill(dis+1,dis+tot+1,0);
    		queue<int> q;q.push(s);
    		dis[s]=0;vis[s]=1;
    		while(!q.empty()){
    			int u=q.front();q.pop();
    			for(int i=first[u];i;i=e[i].nxt){
    				int v=e[i].v;
    				if(e[i].f>0&&!vis[v]){
    					vis[v]=1;dis[v]=dis[u]+1;
    					q.push(v);
    				}
    			}
    		}
    		return vis[t];
    	}
    	inline int dfs(int u,int f){
    		if(u==t||!f) return f;
    		int used=0;
    		for(int &i=cur[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(e[i].f>0&&dis[v]==dis[u]+1){
    				int fl=dfs(v,min(f,e[i].f));
    				used+=fl;f-=fl;
    				e[i].f-=fl;e[i^1].f+=fl;
    				if(!f) break;
    			}
    		}
    		if(f) dis[u]=inf;
    		return used;
    	}
    	inline int dinic(int S,int T,int tp){
    		if(tp) e[2].f=e[3].f=0;
    		int ans=0;s=S;t=T;
    		while(bfs()){
    			memcpy(cur,first,sizeof(first));
    			ans+=dfs(s,inf);
    		}
    		return ans;
    	}
    	inline void init(){
    		fill(first+1,first+tot+1,0);tot=0;
    		cnt=1;
    	}
    }
    using flow::tot;
    using flow::Add;
    using flow::cnt;
    int n,m,s,t,S,T,w[N],pos[N];
    int main(){
    	while(scanf("%d%d",&n,&m)!=EOF){
    		fill(w+1,w+tot+1,0);
    		flow::init();
    		s=++tot;t=++tot;S=++tot;T=++tot;
    		Add(t,s,inf); 
    		for(int i=1,g;i<=m;++i){
    			scanf("%d",&g);
    			tot++;
    			Add(tot,t,inf);
    			w[tot]-=g;w[t]+=g;
    		}
    		for(int i=1,c,d;i<=n;++i){
    			scanf("%d%d",&c,&d);
    			tot++;
    			Add(s,tot,d);
    			for(int j=1,t,l,r;j<=c;++j){
    				scanf("%d%d%d",&t,&l,&r);t++;
    				Add(tot,t+4,r-l);
    				w[t+4]+=l;w[tot]-=l;
    			}
    		}
    		int sum=0;
    		for(int i=1;i<=tot;++i){
    			if(w[i]>0) sum+=w[i],Add(S,i,w[i]);
    			if(w[i]<0) Add(i,T,-w[i]);
    		}
    		if(flow::dinic(S,T,0)!=sum){
    			puts("-1
    ");
    			continue; 
    		}
    		printf("%d
    
    ",flow::e[3].f+flow::dinic(s,t,1));
    	}
    	return 0;
    }
    

    五、有源汇上下界最小流

    和上个问题没什么区别,先跑出一个可行流,然后在残量网络上跑(t ightarrow s)的最大流,将这最大流的路径上所有道路的流量都撤回,依然得到一个可行流。因此答案就是可行流减去反向最大流。

    模板:loj #117

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e4+10;
    const int M=4e5+10;
    const int inf=0x3f3f3f3f;
    namespace flow{
    	struct node{
    		int v,f,nxt;
    	}e[M];
    	int tot;
    	int s,t,cnt=1,cur[N],first[N],vis[N],dis[N];
    	inline void add(int u,int v,int f){
    		e[++cnt].v=v;e[cnt].f=f;e[cnt].nxt=first[u];first[u]=cnt;
    	}
    	inline void Add(int u,int v,int f){
    		add(u,v,f);add(v,u,0);
    	}
    	inline bool bfs(){
    		fill(vis+1,vis+tot+1,0);
    		fill(dis+1,dis+tot+1,0);
    		queue<int> q;q.push(s);
    		dis[s]=0;vis[s]=1;
    		while(!q.empty()){
    			int u=q.front();q.pop();
    			for(int i=first[u];i;i=e[i].nxt){
    				int v=e[i].v;
    				if(e[i].f>0&&!vis[v]){
    					vis[v]=1;dis[v]=dis[u]+1;
    					q.push(v);
    				}
    			}
    		}
    		return vis[t];
    	}
    	inline int dfs(int u,int f){
    		if(u==t||!f) return f;
    		int used=0;
    		for(int &i=cur[u];i;i=e[i].nxt){
    			int v=e[i].v;
    			if(e[i].f>0&&dis[v]==dis[u]+1){
    				int fl=dfs(v,min(f,e[i].f));
    				used+=fl;f-=fl;
    				e[i].f-=fl;e[i^1].f+=fl;
    				if(!f) break;
    			}
    		}
    		if(f) dis[u]=inf;
    		return used;
    	}
    	inline int dinic(int S,int T,int tp){
    		if(tp) e[2].f=e[3].f=0;
    		int ans=0;s=S;t=T;
    		while(bfs()){
    			memcpy(cur,first,sizeof(first));
    			ans+=dfs(s,inf);
    		}
    		return ans;
    	}
    	inline void init(){
    		fill(first+1,first+tot+1,0);tot=0;
    		cnt=1;
    	}
    }
    using flow::tot;
    using flow::Add;
    using flow::cnt;
    int n,m,s,t,S,T,w[N],pos[N];
    int main(){
    	scanf("%d%d%d%d",&n,&m,&s,&t);
    	tot=n;S=++tot;T=++tot;
    	Add(t,s,inf);
    	for(int i=1;i<=m;++i){
    		int u,v,l,r;
    		scanf("%d%d%d%d",&u,&v,&l,&r);
    		Add(u,v,r-l);w[u]-=l;w[v]+=l;
    	}
    	int sum=0;
    	for(int i=1;i<=n;++i){
    		if(w[i]>0) sum+=w[i],Add(S,i,w[i]);
    		if(w[i]<0) Add(i,T,-w[i]);
    	}
    	if(flow::dinic(S,T,0)!=sum){
    		puts("please go home to sleep");
    		return 0;
    	}
    	else printf("%d
    ",flow::e[3].f-flow::dinic(t,s,1));
    	return 0;
    }
    
  • 相关阅读:
    求出123456 8910,数字,显示出来
    Django之Model操作
    AJAX全套
    Python之路【第十六篇】:Django【基础篇】
    Q2Day81
    Django第二重点(thml与django连接)
    react
    es6 和 python 语法比较
    微信
    py库: GUI(tkinter)
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14403469.html
Copyright © 2011-2022 走看看