• 【瞎口胡】上下界网络流


    在部分网络流问题中,会要求边的流量在一个区间 \([l,r]\) 之间,即新增了一个对流量下界的限制。

    这种问题被称为「上下界网络流」。

    如果你不知道什么是网络流,我建议你马上去读这里

    无源汇上下界可行流

    求出一组流 \(f\) 使得每个点流量平衡。\(i\) 点是流量平衡的,当且仅当 \(\sum\limits_{j\neq i} f(j,i) = \sum \limits_{j\neq i} f(i,j)\),即流入流量等于流出流量。

    对于第 \(i\) 条边 \((x_i,y_i,(l_i,r_i))\) 至少需要流 \(l_i\) 的流量,我们不妨将这部分流量流完,然后建立一个「残」网络,该网络中第 \(i\) 条边为 \((x_i,y_i,r_i-l_i)\)

    观察到,如果所有点流量平衡,那么我们就找到了一组可行流。反之,设每个点的净流入量(流入 - 流出)为 \(s_i\),那么:

    • 如果 \(s_i=0\),那么该点流量平衡,我们不用处理这个点。
    • 如果 \(s_i>0\),说明流入的流量过多,我们需要从该点流出 \(s_i\) 的流量到其它点。因此,我们需要一个在残网络上的虚拟源点 \(S'\),然后从 \(S'\) 流出 \(s_i\) 的流量到 \(i\)
    • 如果 \(s_i<0\),说明流出的流量过多,我们需要从其它点接受 \(s_i\) 的流量。因此,我们需要一个在残网络上的虚拟汇点 \(T'\),然后从该点流 \(s_i\) 的流量到 \(T'\)

    最后,在残网络上跑一遍 \(S'\)\(T'\) 的最大流,满流(\(S'\) 流出的流量和 \(T'\) 流入的流量相等)则有一组可行流,否则不存在可行流。

    正确性显然。

    无源汇上下界最小费用可行流

    把最大流改为费用流即可。

    总费用即为费用流的费用加上流满每条边下界的费用。

    有源汇上下界(最小费用)可行流

    区别于无源汇的上下界网络流,有源汇上下界网络流对于源汇 \(S,T\),只需要满足 \(S\) 的净流出量等于 \(T\) 的净流入量。其它点仍然需要流量平衡。

    一种做法是连一条 \((T,S,(0,+\infty))\) 的边,然后就变为了无源汇上下界网络流。\(S\)\(T\) 的流量就是这条边流过的流量。

    例题 1 [AHOI2014/JSOI2014]支线剧情

    题意

    给定 \(n\) 个点和 \(m\) 条边的带权有向图,保证从 \(1\) 开始可以走到全部 \(n\) 个点。每次可以选择一条从 \(1\) 开始的路径,给路径上的每一条边打上一个标记,代价为路径上的边权之和。询问给每条边都打上标记的最小代价。

    \(1 \leq n \leq 300,1 \leq m \leq 5000\),边权 \(w_i\) 满足 \(1 \leq w_i \leq 300\)

    题解

    每条边至少经过一次等价于至少流过 \(1\) 的流量,因此边 \((u,v,w)\) 在网络流图中表示为 \((u,v,(1,+\infty),w)\)。路径可以在任何点结束,于是建立超级汇点 \(T\)(注意与虚拟汇点 \(T'\) 区分,它们不是同一个点),并对于每个点 \(i\) 连接边 \((i,T,(0,+\infty),0)\)

    然后就是一个源点为 \(1\) 汇点为 \(T\) 的有源汇上下界最小费用可行流。

    # include <bits/stdc++.h>
    
    const int N=100010,MAXN=1010,INF=0x3f3f3f3f;
    
    struct Edge{
    	int to,next,v,w;
    };
    int fsum[MAXN],coss;
    
    struct Dinic{
    	Edge edge[N];
    	int head[MAXN],sum;
    	bool vis[MAXN];
    	int dis[MAXN],s,t,cost;
    	Dinic(){
    		sum=1;
    		return;
    	}
    	inline void add(int x,int y,int v,int w){
    		edge[++sum]=(Edge){y,head[x],v,w},head[x]=sum;
    		return;
    	}
    	inline void addedge(int x,int y,int v,int w){
    		add(x,y,v,w),add(y,x,0,-w);
    		return;
    	}
    	inline bool spfa(void){
    		std::queue <int> q; 
    		memset(dis,INF,sizeof(dis)),dis[s]=0;
    		q.push(s);
    		while(!q.empty()){
    			int i=q.front();
    			q.pop(),vis[i]=false;
    			for(int j=head[i];j;j=edge[j].next){
    				int to=edge[j].to;
    				if(edge[j].v&&dis[to]>dis[i]+edge[j].w){
    					dis[to]=dis[i]+edge[j].w;
    					if(!vis[to])
    						q.push(to),vis[to]=true; 
    				}
    			}
    		}
    		return dis[t]<INF;
    	}
    	int dfs(int i,int flow){
    		if(i==t)
    			return flow;
    		vis[i]=true;
    		int maxsum=0;
    		for(int j=head[i];j;j=edge[j].next){
    			int to=edge[j].to;
    			if(vis[to]||!edge[j].v||dis[to]!=dis[i]+edge[j].w)
    				continue;
    			int res=dfs(to,std::min(edge[j].v,flow));
    			flow-=res,maxsum+=res,edge[j].v-=res,edge[j^1].v+=res,cost+=edge[j].w*res;
    			if(!flow)
    				break;
    		}
    		vis[i]=false;
    		if(!maxsum)
    			dis[i]=INF;
    		return maxsum;
    	}
    	inline void solve(void){
    		while(spfa()){
    			dfs(s,INF);
    		}
    		return;
    	}
    }G;
    int n;
    
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')
    		res=res*10+c-48;
    	return res*f;
    }
    
    int main(void){
    	n=read();
    	for(int i=1;i<=n;++i){
    		int d=read();
    		while(d--){
    			int to=read(),ti=read();
    			G.addedge(i,to,INF,ti);
    			++fsum[to],--fsum[i],coss+=ti;
    		}
    	}
        // n+1 S'
        // n+2 T'
        // n+3 T
    	G.s=n+1,G.t=n+2;
    	for(int i=1;i<=n;++i)
    		G.addedge(i,n+3,INF,0);
    	G.addedge(n+3,1,INF,0); // 循环边
    	for(int i=1;i<=n;++i){
    		if(fsum[i]>0){
    			G.addedge(G.s,i,fsum[i],0);
    		}else if(fsum[i]<0){
    			G.addedge(i,G.t,-fsum[i],0);
    		}
    	}
    	G.solve();
    	printf("%d",coss+G.cost);
    	return 0;
    }
    

    有源汇上下界(最小费用)最大流

    首先跑出一组可行流。如果它不存在,那么无解。

    这个时候残量网络一定是流量平衡的。我们把 \(S',T'\) 和所有与它们相连的边删掉,再跑一遍 \(S\)\(T\) 的(最小费用)最大流「榨干」剩下的流量即可。最后和可行流的流量相加。

    当然实现的时候不删也是可以的,入度为 \(0\)\(S'\) 和出度为 \(0\)\(T'\) 显然也不会影响流量...

    有源汇上下界(最小费用)最小流

    考虑反向榨干,从 \(T\)\(S\) 跑最大流,然后用可行流的流量减掉。

  • 相关阅读:
    linq to sql 还没解决的问题
    js暂停函数,想做牛人的菜鸟遇到了问题。
    公司给我拿800/月 干不干啊?ASP.NET程序员
    android の Handler消息传递机制
    android の Activity & View | LayoutInflate
    ActionScript事件侦听addEventListener的参数详解
    java中String的intern方法和equals方法的使用
    java中short,int转换成byte数组及byte数组转换成short,int
    Flex组件中list里面的数据引用
    Flex中设置弹出窗口的弹出效果(alpha值的渐变和scale值的渐变)
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/16050733.html
Copyright © 2020-2023  润新知