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

    刚刚学了有上下界的网络流问题,总结一下。

    大概可以分为4种:

    1、无源汇的可行流

    2、有源汇的可行流

    3、有源汇的最大流

    4、有源汇的最小流


    无源汇的可行流

    想像一条水循环系统,无源汇的可行流就是流量在整张图里循环,每一个节点都满足流量守恒,没有源点和汇点这样的特殊点。

    但是,在这个问题中,每条边都有一个容量下限Bi和一个容量上限Ci。上限好理解,就是普通最大流中的东西,但下限怎么处理?

    想象,如果在这张流网络满足每条边的流量都在【Bi,Ci】内,那么对于每个节点,流经它的流量至少为它的入边的下限和,流出它的流量至少为它出边的下限和。

    怎么限制住这个条件呢?于是我们人为地加入一个源点S和汇点T:

    S:向每个节点连一条边,容量为它入边的下限和

    T:每个节点向T连一条边,容量为它出边的下限和

           对每条其它的边,容量变为Ci-Bi,即为正常的最大流的边。

    为什么能这样呢?

    每条边上的流量可以分为两种:限制流和自由流

            限制流就是为了满足下限而必须存在的流,也就是该边的下限

            自由流即超出下限的那一部分

    我们加入S和T,砍掉其它边的下限,相当于把其它边的限制流抢了过来,由S和T管理,这样子剩下的就是自由流,可以为任意流量。

    这样一来,如果该流网络存在可行流,当且仅当求出S到T的最大流后S的出边都满载,T的入边都满载。


    ZOJ2314 Reactor Cooling

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int maxn=205,maxm=80005,INF=2000000000;
    int inf[maxn],outf[maxn];
    
    inline int read()
    {
    	int out=0,flag=1;char c=getchar();
    	while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
    	while(c>=48&&c<=57) {out=out*10+c-48;c=getchar();}
    	return out*flag;
    }
    
    int head[maxn],nedge=0;
    class EDGE
    {
    	public:
    		int to,next,f,b;
    }edge[maxm];
    
    inline void build(int a,int b,int w,int l)
    {
    	edge[nedge]=(EDGE){b,head[a],w,l};
    	head[a]=nedge++;
    	edge[nedge]=(EDGE){a,head[b],0,l};
    	head[b]=nedge++;
    }
    
    int cur[maxn],S,T,d[maxn];
    bool vis[maxn];
    
    bool bfs()
    {
    	fill(vis,vis+maxn,false);
    	queue<int> q;
    	q.push(S);
    	d[S]=0;vis[S]=true;
    	int u,to;
    	while(!q.empty())
    	{
    		u=q.front();
    		q.pop();
    		for(int k=head[u];k!=-1;k=edge[k].next)
    			if(!vis[to=edge[k].to]&&edge[k].f)
    			{
    				d[to]=d[u]+1;
    				vis[to]=true;
    				q.push(to);
    			}
    	}
    	return vis[T];
    }
    
    int dfs(int u,int minf)
    {
    	if(u==T||!minf) return minf;
    	int flow=0,f,to;
    	if(cur[u]==-2) cur[u]=head[u];
    	for(int& k=cur[u];k!=-1;k=edge[k].next)
    		if(d[to=edge[k].to]==d[u]+1&&(f=dfs(to,min(minf,edge[k].f))))
    		{
    			edge[k].f-=f;
    			edge[k^1].f+=f;
    			flow+=f;
    			minf-=f;
    			if(!minf) break;
    		}
    	return flow;
    }
    
    int maxflow()
    {
    	int flow=0;
    	while(bfs())
    	{
    		fill(cur,cur+maxn,-2);
    		flow+=dfs(S,INF);
    	}
    	return flow;
    }
    
    int main()
    {
    	int CNT=read();
    	while(CNT--)
    	{
    		fill(head,head+maxn,-1);
    		fill(inf,inf+maxn,0);
    		fill(outf,outf+maxn,0);
    		nedge=0;
    		int N=read(),M=read(),a,b,l,r;
    		S=0;T=N+1;
    		for(int i=1;i<=M;i++)
    		{
    			a=read();
    			b=read();
    			l=read();
    			r=read();
    			outf[a]+=l;
    			inf[b]+=l;
    			build(a,b,r-l,l);
    		}
    		int cnt=0,flow;
    		for(int i=1;i<=N;i++) build(S,i,inf[i],0),cnt+=inf[i];
    		for(int i=1;i<=N;i++) build(i,T,outf[i],0);
    		flow=maxflow();
    		//cout<<cnt<<' '<<flow<<endl;
    		if(cnt==flow)
    		{
    			printf("YES
    ");
    			for(int i=0;(i>>1)<M;i+=2) printf("%d
    ",edge[i^1].f+edge[i^1].b);
    		}
    		else printf("NO
    ");
    	}
    	return 0;
    }
    



    有源汇的可行流

    与无源汇的可行流类似,有源汇的可行流就是多了个源点和汇点。这个时候我们可以从另一个角度来看网络流:虽然S和T不满足流量限制,但如果我们人为连一条由T到S的容量为正无穷的边,那么它就转化成了所有点都遵循容量限制的无源汇的流网络。

    那么我们再加一个超级源点S‘和超级汇点T’【就相当于上边的人为加入的源汇点】,就可以转化为无缘汇的可行流啦。


    有源汇的最大流

    有源汇的最大流就是在求出有源汇的可行流之后去掉加入的T到S的无穷大的边,然后再在求完可行流的残量网络中求一遍由S到T的最大流即为最大流

    最后结果为可行流中边T->S的流量+新一次最大流中新增的流量。


    有源汇的最小流

    求法:

    1、同样加一个超级源S‘,超级汇T’,【这个时候不加边T->S】求一遍S‘到T’最大流

    2、加入边T->S【正无穷】,再求一遍S‘到T’最大流。

    3、若S‘出去的边不满流,T’进来的边不满流,则无解,否则解为T->S边的流量。【这些流量为使可行流成立必须加的流】



    我很辣鸡,只知道这些。

  • 相关阅读:
    CentOS yum 安装svn1.8
    js 替换掉汉字 和替换非汉字 比较时间JS
    PhpStorm 10 破解
    html中link的用法
    CSS3:nth-child()伪类选择器
    提示的小三角
    css 高度自适应
    <input type="file" />浏览时只显示指定文件类型
    MySQL查询表内重复记录
    扒站小工具
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282903.html
Copyright © 2011-2022 走看看