网络流问题
Posted overrate-wsj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流问题相关的知识,希望对你有一定的参考价值。
网络流
网络流问题常见的求解目标有最大流(最小割)、最小费用最大流、上下界可行流等
最小割
最大流还有一个很重要的应用,就是求最小割,以下是一些定理,其实这些和二分图匹配里面的有点相似:
最小割 = 最大流
最大点权覆盖集 = 最小割
最小点权独立集 = 总权值 - 最大点权覆盖集
最小割的定义:把网络划分成两个集合,s,t使得在s集合和在t集合中的任意两点互相不相连,去掉的边就是割边,而这些割边代价总和就是割,最小割就是代价最小的划分方法
一类最小割的题目还是挺明显,如果是要把存在点划分两个集合,求最小代价之类的,就很明显是最小割了
最小割有一些挺经典的模型:
1、平面图求最小割:
这个做法就是,把平面每一部分面积当成点,然后相邻块连边,然后增设源点s和汇点t,分别在原来的入口出口的另一对角线上,和相应两部分边连边,这时候,每一个最小割,其实就是一个s到t的路径,那么求最短路就是最小割了
2、最小权闭合
这个做法是源点s连向正权值,负权值连向汇点t,之间关系连边容量INF,求出最小割之后,这个最小割就是最少损失,然后总权值 - 最小割得到的就是最大权闭合图的权值
关于最小割输出路径
根据题意,如果要s集合尽量多,就从t集合dfs,反之则从s集合dfs,不经过没有容量的边即可
费用流
一类K覆盖问题:
这类问题一般表现为,一个区间或者一些点,每个可以最多被覆盖k次,然后有一些特殊的边可以走,但是只能走一次,这时候要求k覆盖后的最大权值
其实就是费用流,建边把特殊边建起来,对应相应费用,然后其他边直接相连,费用为0,注意由于是要求最大代价,而算法是求最小费用,其实和KM匹配求最小一样的思路,把边权弄成负数,跑一下即可
网络流的一些特殊问题
上下界网络流:
无源无汇有上下界最大流:
这个要根据流量平衡来搞,建图先把边容量定成上限up - 下限down,然后每一个点,记录下流入流量和流出流量,然后设一个超级源s,超级汇t,s连接流量正的点,流量负的点连向t,然后跑最大流,跑完之后如果从s流出的流量都是满流,就是有解,每个边的真实流量就为当前边流量,加上原来的下限
有源有汇有上下界最大流:
建图方法一致,不过要多连上一条t->s容量为INF的边,这样跑一下最大流,t->s的流量就是答案
有源有汇有上下界最小流:
也是一样,不过t->s先不连,先求一次最大流,然后在连t->s,在做一次最大流把残余流量充分利用,然后t->s的流量就是答案
分层网络流:
这类题,以时间为单位,这样对于每个时间点就要建一层结点,时间上限不是很大的话,就可以每多一个时间点,就多一层结点,在原来的图上继续增广
混合图找欧拉回路:
欧拉回路入度等于出度,然后无向图先任意定向,然后记录每个点的度数和,度数和 / 2就是需要调整的边数,然后把源点连向正的,负的连向汇点,然后中间的边就是连无向边,因为只有无向边可以调整,然后跑一下最大流即可
增加哪些边会使得最大流增加:
这类问题其实就是对于满流的边,如果左边源点到他和他到汇点,能有一个残量网络,这条边就是可以增加的,利用两个dfs,分别从源点汇点出发即可
最大密度子图:
先要记录下每个结点的度数
利用二分搜索来搜索答案g,然后根据这个建图判断,判断的方式为:
源点与原图中每一个点连一条容量为m的边。原图中每一个点与汇点连一条容量为m+2*g-度数的边,再将原图中的无向边拆成两条有向边,容量都设为1.然后对此图求最大流,最后将(n*m-maxflow)/2 与0比较大小,如果它大于0,l = g,
否则r = g
POJ 3281 Dining
题意:
一个人有喜欢的饮料跟食物,现在给出这些关系,问最多能有多少人得到喜欢的饮料跟食物
思路:
网络流的题目往往难在建模,我们很容易想到的一种建模方法就是源点-食物-人-饮料-汇点
但是这样的话可能一个人可以满足多次,所以为了加上人的这个限制条件,就需要将人拆点,并连上一条权值为1的边
拆点也是对于点限制的很好的解决方法
#include<iostream> #include<algorithm> #include<cstring> #include<queue> using namespace std; const int inf=1<<30; struct Graph{ static const int M=100010,N=10010; int dis[N],cur[N],head[N],cnt; void Grap(){ cnt=-1; memset(head,-1,sizeof head); } struct edge{ int to,val,next; }e[M<<1]; void _add(int u,int v,int val){ cnt++; e[cnt].next=head[u]; e[cnt].to=v; e[cnt].val=val; head[u]=cnt; } void add(int u,int v,int val){ _add(u,v,val); _add(v,u,0); } bool bfs(int s,int t){ queue<int> q; memset(dis,-1,sizeof dis); dis[s]=0; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to; if(dis[v]==-1&&e[i].val>0){ dis[v]=dis[u]+1; q.push(v); } } } return dis[t]!=-1; } int dfs(int s,int t,int maxflow){ if(s==t)return maxflow; int res=0; for(int& i=cur[s];i!=-1;i=e[i].next){ int v=e[i].to; if(dis[v]!=dis[s]+1||e[i].val<=0||res>=maxflow)continue; int f=dfs(v,t,min(e[i].val,maxflow-res)); e[i].val-=f; e[i^1].val+=f; res+=f; } return res; } int Dinic(int s,int t){ int ans=0; while(bfs(s,t)){ memcpy(cur,head,sizeof head); ans+=dfs(s,t,inf); } return ans; } }mode; int main() { mode.Grap(); int n,a,b; scanf("%d%d%d",&n,&a,&b); for(int i=1;i<=n;i++){ int f,d,x; scanf("%d%d",&f,&d); for(int j=1;j<=f;j++){ scanf("%d",&x); mode.add(x,a+i,1); } for(int j=1;j<=d;j++){ scanf("%d",&x); mode.add(a+n+i,2*n+a+x,1); } } for(int i=1;i<=n;i++) mode.add(a+i,a+n+i,1); for(int i=1;i<=a;i++) mode.add(0,i,1); for(int i=1;i<=b;i++) mode.add(2*n+a+i,2*n+a+b+1,1); cout<<mode.Dinic(0,2*n+a+b+1)<<endl; return 0; }
以上是关于网络流问题的主要内容,如果未能解决你的问题,请参考以下文章
Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题
Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题
java缓冲字符字节输入输出流:java.io.BufferedReaderjava.io.BufferedWriterjava.io.BufferedInputStreamjava.io.(代码片段