最大流最小割问题
本文就不对最大流最小割问题定义了,当然也就不证明最大流 = 最小割了。
本文介绍一种求最大流的方法。SAP 算法(最短增广路算
法)。最短增广路算法(Shortest Augmenting Path Algorithm),即每次寻找包含弧的个数最少的增广路进行增广,可以证明,此算法最多只需要进行
最终的时间复杂度为
O(|V|2|E|)
,但在实践中,时间复杂度远远小于理论值(特别是加了优化之后),因此还是很实用的。
距离标号:对于每个顶点
u
赋予一个非负整数值
(1):d(t)
=0;
(2):
对于残留网络中一条弧
(u,v)满足,d(u)≤d(v)+1
。
如果残留网络
G
中的一条弧
实现中我们用一个 DFS 实现这个寻找最短增广的过程,如果无法继续向前,那么我们就修改当前结点的距离标号。
SAP算法有两个很好的优化GAP优化和当前弧优化。
Gap优化:我们可以注意到由于残留网络的修改只会使
当前弧优化: 可以注意到一个事实:如果说在某次迭代中从
i
出发的弧
下面附上SAP代码
tot=n+m+1;
bool p;
vh[0]=tot+1;
int x=0,aug=oo,flow=0;
while (dis[0]<tot){
p=0;//这个是是否有找到允许弧的标志
his[x]=aug;//标记,以便以后返回这个值
for(int i=di[x];i;i=next[i]){
if (v[i]&&dis[t[i]]+1==dis[x]){//找到允许弧
p=1;
di[x]=i;//标记当前弧
pre[t[i]]=x;//记录前驱
aug=min(aug,v[i]);
x=t[i];
if (x==tot){//找到增广路
ans+=aug;
while (x!=0){
x=pre[x];
v[di[x]]-=aug;
v[di[x] ^ 1]+=aug;
}
aug=∞;
}
break;//找到允许弧则退出查找
}
}
if (!p){
int k,min;
min=tot;//没有允许弧了,需要重标号
for(int i=head[x];i;i=next[i]){
if (v[i]&&min>dis[t[i]]){
min=dis[t[i]];
k=i;
}
}
--vh[dis[x]];//GAP 优化
if (vh[dis[x]]==0) break;
vh[++min]++;
dis[x]=min;
di[x]=k;
if (x){
x=pre[x];//返回上一层
aug=his[x];//知道之前记录这个值的用处了吧
}
}
}
}
最大流
给大家一道比较老的题目,相信很多人都已经做过。
《POJ3281Dining》
【题目大意】
有
F
种食物和
【建模方法】
此题的建模方法比较有开创性。以往一般都是左边一个点集表示供应并与源相连,右边一个点集表示需求并与汇相连。现在不同了,供应有两种资源,需求仍只有一个群体,怎么办?其实只要仔细思考一下最大流的建模原理,此题的构图也不是那么难想。最大流的正确性依赖于它的每一条
s→t
流都与一种实际方案一一对应。那么此题也需要用s-t流将一头牛和它喜欢的食物和饮料“串”起来,而食物和饮料之间没有直接的关系,自然就想到把牛放在中间,两边是食物和饮料,由
s,t
将它们串起来构成一种分配方案。至此建模的方法也就很明显了:每种食物
i
作为一个点并连边
本文还给读者提供一道题。
《WOJ1124FootballCoach》 。
【题目大意】
有
N
支球队,互相之间已经进行了一些比赛,还剩下
【建模方法】
首先,所有跟球队
N
相关的比赛都要让球队
【本题拓展】
如果不允许平局存在,该如何构图?
《SPOJ962IntergalacticMap》
【题目大意】
在一个无向图中,一个人要从
A
点赶往
【建模方法】
由于每个点只能走一次,似乎最短路之类的算法不能用,只有往网络流上靠。将每个点
i
拆成两个点
最小割
最大点权独立集问题
《HOJ 2713 Matrix1》
【题目大意】
一个N*M的网格,每个单元都有一块价值Cij的宝石。问最多能取多少价值的宝石且任意两块宝石不相邻。
(1<=N,M<=50,0<=Cij<=40000)
【建模方法】
经典的最大点权独立集问题。转化为最小点权覆盖集:先将网格黑白染色,从源点到每个黑点有一条边,从每个白点到汇点有一条边,容量均为相应宝石的价值。每个黑点向与其相邻的四个白点连边,容量为
∞
。设最小割为
《POJ 1815 Friendship》
【题目大意】
现代社会人们都靠电话通信。
A与B
能通信当且仅当
A知道B
的电话号或者
A知道C
的电话号且
C与B
能通信。若
A知道B
的电话号,那么
B也知道A
的电话号。然而不好的事情总是会发生在某些人身上,比如他的电话本丢了,同时他又换了电话号,导致他跟所有人失去了联系。现在给定
N
个人之间的通信关系以及特定的两个人
【建模方法】
1:把除了
s和t
外其他所有点连一条弧
(i,i′,1)
(这条弧表示i没有丢失自己的联系人,没有这条弧,说明
i
不能喝他的朋友联系了。)
2:如果
3:如果
i知道j
的号码 ,则连弧
(i′,j,∞)
和
(j′,i,∞)
求一个最小割,然后从
1到n
枚举删点,若删除该点后做的最小割不变则证明这个点不在答案里。反之则要加入答案内。
《ZOJ 2532 Internship》
【题目大意】
有
N
个城市,
【建模方法】
此题要求找出这样一条边,增加它的容量可以导致最大流的增加。最直观的做法是先求一次最大流记为
ans
,然后枚举每条边的容量加1再求最大流,看是否大于
ans
。但是这样做复杂度太高。不妨换种思路,假设现在满流了,我们会考虑增加哪些边的容量呢?显然是满流的边。那么增加了这条边的容量之后,总流量在什么条件下会增加呢?产生一条新的增广路.搞清楚上面的问题之后就会得到一个复杂度较好的算法,枚举做完最大流之后的每条边
u→v
,如果这条边满流,并且存在
S到u
的增广路以及
v到T
的增广路,那么增加这条边的容量就会增大总流量。判断
S到u
有无增广路可以先用dfs预处理一下,从
S
点开始只沿非满流的边走,能够到达某个位置就说明
最小费用流
《HOJ 2715 Matrix3》
【题目大意】
一个
N∗N
的网格,每个单元都有一个价值
Vi
的宝物和一个高度
Hi
。现在ZhouGuyue要作至多K次旅行,每次旅行如下:他可以借助bin3的直升机飞到任意一个单元,之后他每次只能向相邻的且高度比当前所在格子低的格子移动。
当他移动到一个边界的格子上时,他可以跳出这个网格并完成一次旅行。旅行中所到之处的宝物他可以全部拿走,一旦拿走原来的格子里就没有宝物了。问他最多能拿走价值多少的宝物。
(1<=N<=50,0<=K<=50,0<=Vi<=10000)
【建模方法】
将每个格子i拆成两个点i′,i′′并加边(i′,i′′,1,−Vi),(i′,i′′,∞,0),(s,i′,∞,0);对相邻的四个格子j,若Hi>Hj则加边(i′′,j′,∞,0);若格子i在边界上则加边(i′′,t,∞,0)
。
限制增广次数小于等于
K
求最小费用流即可。
《剪刀石头布》
【题目大意】
竞赛图中一些边已经给定,另一些边需要你去定向。问合理安排这些边的方向后,长为3的环最多能有多少(一个长为3的环叫做“剪刀石头布”现象)。
【建模方法】
此题运用了“补集转化思想”。考虑集合
a,b,c
不构成剪刀石头布的情况,那么三个点中一个入度为2,一个出度为2,一个入度出度均为1。不妨以入度为2的点作思考,那么总的非剪刀石头布的情况数便为∑
((indegree[i]2)
,剪刀石头布的情况数便为
S=(N3)−∑((indegree[i]2)=N(N−1)(N−2)/6–M/2–(∑indegree[i]2)/2
。注意到
N(N−1)(N−2)/6–M/2
是常数,那么要想让S最大,只要
(∑indegree[i]2)/2
最小就可以了。这里产生了凸费用函数
f(x)=x2
,如何转化为最小费用流模型呢?每条待定向的边
i
作为一个点并加边