zoukankan      html  css  js  c++  java
  • 上下界网络流

    上下界网络流

    给你一张网络 , 对于一条边有两个限制 \(low(u,v),high(u,v)\),要求实际通过的流量为 \(low \le f(u,v) \le high\). 在此基础上满足流量守恒(流出的流量 = 流入的流量)

    概述

    无源汇上下界网络流

    移项可得 \(low(u,v) \le f(u,v) \le high(u,v) \Rightarrow 0 \le f(u,v) \le high(u,v) - low(u,v)\) , 每条边都变成 \(high - low\) 的形式,表示下界跑满。容易发现,按照这种做法,对于每一个节点 \(pos\),一共有 \(in = \sum low(u,pos)\) 的流量流入;一共有 \(out = \sum low(pos,u)\) 的流量流出。

    \(out\)\(in\) 的差值指示这这个点能不能遵循流量守恒。如果入流量多了,我们从虚拟源点 \(S\) 里面连一条容量为 \(in - out\) 的边到 \(pos\) , 否则连到 \(T\) 去。

    跑一边最大流,如果满足流量守恒,也就是从 \(S\) 出来的权值 = \(T\) 接收到的权值,那么残量网络减少的值 + 下界就是一组解。

    有源汇上下界网络流

    这个简单,连一条边 \(T\rightarrow S\),边权为 \([0,\inf]\) , 这样就转换成无源汇问题。

    理解:\(S,T\) 这两个点非常特殊,可以不用满足流量守恒。

    有源汇上下界最大流

    先跑一次无源汇上下界网络流构造出可行流,然后在残量网络上跑一次从 \(S\)\(T\) 的最大流,把能推的流量推到 \(T\) 那里去。

    有源汇上下界最小流

    和最大流差不多,只不过是从 \(T\)\(S\) 跑一次最大流把能送回来的流量送回来。

    例题

    模板题:P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

    P4843 清理雪道

    这个题乍一看是最小链覆盖问题,但是传递闭包之后的边规模达到了惊人的 \(O(n^3)\) , 无法进行二分图匹配。

    有一个条件:每条边必须经过一次,也就是下界。最小流即可解决。

    堆一个模板代码:

    //{{{ template
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <set>
    #include <vector>
    #include <map>
    #include <cassert>
    #include <deque>
    #include <cmath>
    using namespace std;
    #define int long long
    inline int gi(){
    	char tmp=getchar();int ans=0,flag=1;
    	while(!isdigit(tmp)){
    		if(tmp == '-') flag = -1;
    		tmp = getchar();
    	}
    	while(isdigit(tmp)){
    		ans = ans * 10 + tmp - '0';
    		tmp = getchar();
    	}
    	return ans * flag;
    }
    inline void in(int &x){x=gi();}
    inline void in2(int &x,int &y){in(x),in(y);}
    inline void in3(int &x,int &y,int &z){in(x),in(y),in(z);}
    inline void in4(int &x,int &y,int &z,int &a){in3(x,y,z),in(a);}
    inline void smax(int &x,int y){x=max(x,y);}
    inline void smin(int &x,int y){x=min(x,y);}
    #define Mem(arr,v) memset(arr,v,sizeof arr)
    #define Copy(arr,goal) memcpy(arr,goal,sizeof goal)
    #define For(i,a,b) for(int i=(int)(a);i<=(int)(b);++i)
    // }}}
    const int N = 42 * 42 * 30;
    struct Edg{
    	int v,flow,nxt;
    }Edge[2002000];
    int Head[N],Cur[N];
    int Bal[N],G[N];
    int cnt = 1;
    void Add(int u,int v,int lower,int upper){
    	Bal[v] += lower; Bal[u] -= lower;
    	// printf("%d %d %d~%d\n",u,v,lower,upper);
    	Edge[++cnt].v = v;Edge[cnt].nxt = Head[u]; Edge[cnt].flow = upper - lower; Head[u] = cnt;
    	Edge[++cnt].v = u;Edge[cnt].nxt = Head[v]; Edge[cnt].flow = 0; Head[v] = cnt;
    }
    int Dep[N];
    int S = N-2 , T = N - 1;
    
    int Bfs(){
    	Mem(Dep,0);Copy(Cur,Head);
    	queue<int> Q;
    	Q.push(S);Dep[S] = 1;
    	while(!Q.empty()){
    		int pos = Q.front(); Q.pop();
    		for(int i = Head[pos]; i; i = Edge[i].nxt){
    			int arr = Edge[i].v;
    			// if(arr == T){
    				// printf("%lld ",pos);
    			// }
    			if(!Dep[arr] && Edge[i].flow){
    				Dep[arr] = Dep[pos] + 1;
    				Q.push(arr);
    			}
    		}
    	}
    	return Dep[T];
    }
    int Dfs(int pos,int flow){
    	if(pos == T){
    		return flow;
    	}
    	int used = 0;
    	for(int i = Cur[pos];i;i = Edge[i].nxt){
    		Cur[pos] = i;
    		int arr = Edge[i].v;
    		if(Dep[arr] == Dep[pos] + 1 && Edge[i].flow){
    			int tmp = Dfs(arr,min(flow - used , Edge[i].flow));
    			if(tmp){
    				Edge[i].flow -= tmp ; Edge[i ^ 1].flow += tmp;
    				used += tmp;
    			}
    			if(used == flow) return used;
    		}
    	}
    	return used;
    }
    int Dinic(){
    	int ans = 0;
    	while(Bfs())
    		ans += Dfs(S,1e9 + 7);
    	return ans;
    }
    int n,m;
    signed main()
    {
    #ifdef NICEGUODONG
    	freopen("data.in","r",stdin);
    #endif
    	int n = gi();
    	int fs = 0 ,ft = n + 1;
    	int inf = 1e8;
    	For(i,1,n){
    		int m = gi();
    		For(j,1,m){
    			int a = gi();
    			Add(i,a,1,1e8);
    		}
    	}
    	For(i,1,n) Add(fs,i,0,inf);
    	For(i,1,n) Add(i,ft,0,inf);
    	For(i,0,n+1){
    		if(Bal[i] > 0)
    			Add(S,i,0,Bal[i]);
    		else if (Bal[i] < 0){
    			Add(i,T,0,-Bal[i]);
    			// printf("%d %d %d\n",i,T,Bal[i]);
    		}
    	}
    	Add(ft,fs,0,inf);
    	Dinic();
    	int ans = Edge[cnt].flow;
    	Edge[cnt].flow = Edge[cnt-1].flow = 0;
    	T = fs, S = ft;
    	printf("%lld\n",ans - Dinic());
    	return 0;
    }
    
  • 相关阅读:
    随机森林算法
    读论文《BP改进算法在哮喘症状-证型分类预测中的应用》
    Spss22安装与破解步骤
    python安装pip、numpy、scipy、statsmodels、pandas、matplotlib等
    windows下Python三步安装pip
    LNMP环境下配置PHP错误信息提示
    SAE临时文件读写例子 SAE_TMP_PATH
    新浪sae 微信公众平台 输出 返回 打印对象
    PHP 易混 知识
    thinkphp tp5 模板文件 循环输出 bootstrap 模态框 弹窗 获取 微信媒体文件素材 media_id
  • 原文地址:https://www.cnblogs.com/guodong2005/p/15092246.html
Copyright © 2011-2022 走看看