• 有上下界的网络流


    有上下界的网络流

    1.无源汇的可行流

    首先一个无源汇的可行流必须要满足每个点的流量平衡,我们记为(f_{out}=f_{in})

    通常,网络流这限制了上界,而无法限制下界。所以对于下界,我们的处理方法就是先让所有边的流量达到下界(d),然后将一条流量在([d,u])的边的流量设为(u-d)。这样就没有下界的问题了。

    显然,这么操作后的结果很可能不是一个可行流,所以我们需要继续调整。

    对于一个点(v),设调整后它获得的入边和出边的流量分别为(g_{in},g_{out}),原始入边和出边的下界之和分别为(d_{in},d_{out})。因为流量平衡,所以(g_{in}+d_{in}=g_{out}+d_{out})。假设(d_{out}>d_{in}),则(g_{in}-g_{out}=d_{out}-d_{in}),也就是说流入的流量要大于流出的流量,反之也是一样的道理。

    然后对于(d_{out}>d_{in})的点,我们建立超级源连向它,流量为(d_{out}-d_{in}),表示它要向外额外输出流量;反过来就建立超级汇,将(d_{out}<d_{in})的点连向超级汇,表示它要额外输入流量。

    原图上的边就按上面所说的方式处理。

    仔细一想就会发现,超级源的所有出边的流量和与超级汇的所有入边流量和相同如果我们跑一边SAP发现满流,则存在可行流。每条边的流量为残余网络中的流量+下界

    2.有源汇的可行流

    首先有源汇与无源汇的区别在于:有源汇可行流中,源点和汇点不一定流量平衡,但是源点多余的流量=汇点的多余流量

    解决有源汇问题时我们通常将其转化为无源汇的问题,具体方法就是连一条((T,S,infty,0))的边。这样的话,汇点多余的输出就可以通过这条边进入源点。

    然后我们建立超级源SS与超级汇TT,之后判断是否有可行流就用1的方法。

    3.有源汇的最大/最小流

    处理这类问题时我们先用2的方法做一遍。

    此时的源汇之间的流量就是那条无穷边的流量,我们记为(w)。注意到(w)不一定是最大/最小流,所以我们还需做下面的事情。

    最大流

    因为(S)(T)之间可能存在富余的边,导致没有得到最大流。

    我们将超级源和超级汇以及与他们相连的边拆掉,然后在残余网络上在跑一次最大流。得到新的最大流与(w)的和就是答案。

    为什么跑最大流的时候不会破坏流量平衡?

    因为残余网络是流量平衡的(除了源汇点),在跑最大流的时候每个中间节点流入的流量与流出的流量是相同的,所以依然满足流量平衡。

    最小流

    法1:与最大流类似。我们先得到了(w)之后我们尽量减少(S)(T)之间的流量。于是我们拆了超级源和超级汇之后“反着”跑最大流,也就是拿(T)当源点,(S)当汇点跑最大流。可以这么理解:反边增加流量相当于正边减少流量。答案就是(w)-最大流。

    法2:当然也可以不用改变图,直接反着跑最大流,答案就是(INF)-最大流。这里(INF)就是我们((T,S,infty,0))的上界。

    正确性:首先我们设我们按法1得到的最大流为(k),那么法2得到的最大流为(k+(INF-w))。因为((T,S,infty,0))流了(w),所以其反边有(INF-w)的流量。

    答案(=INF-(k+(INF-w))=w-k)。与法1等价的。

    4.例题(Loj)

    无源汇有上下界可行流

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 205
    #define M 15005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m;
    int S,T;
    int flow[N];
    struct load {
    	int to,next,flow;
    }s[M<<2];
    int h[N],cnt=1;
    void add(int i,int j,int flow) {
    	s[++cnt]=(load) {j,h[i],flow};h[i]=cnt;
    	s[++cnt]=(load) {i,h[j],0};h[j]=cnt;
    }
    int gap[N],dis[N];
    ll dfs(int v,int maxf) {
    	if(v==T) return maxf;
    	int ret=0;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(dis[to]+1==dis[v]&&s[i].flow) {
    			int dlt=dfs(to,min(maxf-ret,s[i].flow));
    			s[i].flow-=dlt;
    			s[i^1].flow+=dlt;
    			ret+=dlt;
    			if(ret==maxf||dis[S]==n+2) return ret;
    		}
    	}
    	if(!(--gap[dis[v]])) dis[S]=n+2;
    	gap[++dis[v]]++;
    	return ret;
    }
    int sap() {
    	gap[0]=n+2;
    	int ans=0;
    	while(dis[S]<n+2) ans+=dfs(S,1<<29);
    	return ans;
    }
    int D[M];
    int main() {
    	n=Get(),m=Get();
    	T=n+1;
    	int a,b,u,d;
    	for(int i=1;i<=m;i++) {
    		a=Get(),b=Get(),d=Get(),u=Get();
    		add(a,b,u-d);
    		flow[a]-=d;
    		flow[b]+=d;
    		D[i]=d;
    	}
    	for(int i=1;i<=n;i++) {
    		if(flow[i]>0) add(S,i,flow[i]);
    		else if(flow[i]<0) add(i,T,-flow[i]);
    	}
    	sap();
    	for(int i=h[S];i;i=s[i].next) {
    		if(s[i].flow) {
    			cout<<"NO";return 0;
    		}
    	}
    	cout<<"YES
    ";
    	for(int i=1;i<=m;i++) {
    		cout<<D[i]+s[(i*2)^1].flow<<"
    ";
    	}
    	return 0;
    }
    

    有源汇有上下界最大流

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 505
    #define M 250005
    #define INF (int)1e8
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m;
    int S,T;
    int st,ed;
    struct load {
    	int to,next;
    	int flow,d;
    }s[M<<1];
    int h[N],cnt=1;
    void add(int i,int j,int d,int u) {
    	s[++cnt]=(load) {j,h[i],u-d,d};h[i]=cnt;
    	s[++cnt]=(load) {i,h[j],0,d};h[j]=cnt;
    }
    int gap[N],dis[N];
    
    int dfs(int v,int maxf) {
    	if(v==T) return maxf;
    	int ret=0;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(s[i].flow>0&&dis[to]+1==dis[v]) {
    			int dlt=dfs(to,min(maxf-ret,s[i].flow));
    			s[i].flow-=dlt;
    			s[i^1].flow+=dlt;
    			ret+=dlt;
    			if(dis[S]>=n+2||ret==maxf) return ret;
    		}
    	}
    	if(!(--gap[dis[v]])) dis[S]=n+2;
    	gap[++dis[v]]++;
    	return ret;
    }
    
    int sap() {
    	memset(dis,0,sizeof(dis));
    	memset(gap,0,sizeof(gap));
    	gap[0]=n+2;
    	int ans=0;
    	while(dis[S]<n+2) ans+=dfs(S,1<<29);
    	return ans;
    }
    
    int flow[N];
    int main() {
    	n=Get(),m=Get();
    	st=Get(),ed=Get();
    	int a,b,u,d;
    	for(int i=1;i<=m;i++) {
    		a=Get(),b=Get(),d=Get(),u=Get();
    		add(a,b,d,u);
    		flow[a]-=d;
    		flow[b]+=d;
    	}
    	int tag=cnt+1;
    	int sum=0;
    	add(ed,st,0,INF);
    	S=0,T=n+1;
    	for(int i=1;i<=n;i++) {
    		if(flow[i]>0) add(S,i,0,flow[i]),sum+=flow[i];
    		else if(flow[i]<0) add(i,T,0,-flow[i]);
    	}
    	int now=sap();
    	if(now!=sum) {cout<<"please go home to sleep";return 0;}
    	S=st,T=ed;
    	int ans=0;
    	for(int i=h[ed];i;i=s[i].next) {
    		if(s[i].flow>1e7) {
    			ans+=s[i^1].flow;
    			break;
    		}
    	}
    	for(int i=tag;i<=cnt;i++) s[i].flow=0;
    	ans+=sap();
    	cout<<ans;
    	return 0;
    }
    
    

    有源汇有上下界最小流

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 50005
    #define M 200005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m,st,ed;
    int S,T;
    struct load {
    	int to,next;
    	ll flow;
    }s[M<<1];
    int h[N],cnt=1;
    void add(int i,int j,ll flow) {
    	s[++cnt]=(load) {j,h[i],flow};h[i]=cnt;
    	s[++cnt]=(load) {i,h[j],0};h[j]=cnt;
    }
    int flow[N];
    int dis[N],gap[N];
    int cur[N]; 
    
    ll dfs(int v,ll maxf) {
    	if(v==T) return maxf;
    	ll ret=0;
    	for(int &i=cur[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(s[i].flow&&dis[to]+1==dis[v]) {
    			ll dlt=dfs(to,min(maxf-ret,s[i].flow));
    			s[i].flow-=dlt;
    			s[i^1].flow+=dlt;
    			ret+=dlt;
    			if(ret==maxf||dis[S]>=n+2) return ret;
    		}
    	}
    	if(!(--gap[dis[v]])) dis[S]=n+2;
    	gap[++dis[v]]++;
    	cur[v]=h[v];
    	return ret;
    }
    
    ll sap() {
    	memset(dis,0,sizeof(dis));
    	memset(gap,0,sizeof(gap));
    	gap[0]=n+2;
    	ll ans=0;
    	for(int i=0;i<=n+2;++i) cur[i]=h[i];
    	while(dis[S]<n+2){
    		ans+=dfs(S,1<<29);
    	}
    	return ans;
    }
    
    int main() {
    	n=Get(),m=Get();
    	st=Get(),ed=Get();
    	int a,b,u,d;
    	for(int i=1;i<=m;i++) {
    		a=Get(),b=Get(),d=Get(),u=Get();
    		add(a,b,u-d);
    		flow[b]+=d;
    		flow[a]-=d;
    	}
    	
    	add(ed,st,1ll<<50);
    	int tag=cnt+1;
    	S=0,T=n+1;
    	ll sum=0;
    	for(int i=1;i<=n;i++) {
    		if(flow[i]>0) add(S,i,flow[i]),sum+=flow[i];
    		else if(flow[i]<0) add(i,T,-flow[i]);
    	}
    	ll now=sap();
    	
    	if(now!=sum) {cout<<"please go home to sleep";return 0;}
    	
    	for(int i=tag;i<=cnt;i++) s[i].flow=0;
    	S=ed,T=st;
    	cout<<(1ll<<50)-sap();
    	
    	return 0;
    }
    
    
  • 相关阅读:
    内置函数filter()和匿名函数lambda解析
    time&datetime模块详解
    python学习笔记:*args和**kwargs使用原理?
    python学习笔记:深浅拷贝的使用和原理
    python传参是传值还是传引用
    第215天:Angular---指令
    第214天:Angular 基础概念
    第213天:12个HTML和CSS必须知道的重点难点问题
    第212天:15种CSS居中的方式,最全了
    第211天:git和github的区别和使用详解
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10067618.html
Copyright © 2020-2023  润新知