网络流例题及构图选讲

最大流最小割问题

本文就不对最大流最小割问题定义了,当然也就不证明最大流 = 最小割了。

本文介绍一种求最大流的方法。SAP 算法(最短增广路算
法)。最短增广路算法(Shortest Augmenting Path Algorithm),即每次寻找包含弧的个数最少的增广路进行增广,可以证明,此算法最多只需要进行|v||s|/2次增广。并且引入距离标号的概念,可以在的时间 O(|v|) 里找到一条最短增广路。
最终的时间复杂度为 O(|V|2|E|) ,但在实践中,时间复杂度远远小于理论值(特别是加了优化之后),因此还是很实用的。
距离标号:对于每个顶点 u 赋予一个非负整数值d(u)来表示 u t的”距离“远近,当然 d(u) 满足以下条件。
(1):d(t) =0;
(2): 对于残留网络中一条弧 (u,v)d(u)d(v)+1
如果残留网络 G 中的一条弧(u,v)满足 d(u)=d(v)+1 我们称 (u,v) 是允许弧,由允许弧组成的一条 st 路径是允许路。显然,允许路是残留网络中的一条最短增广路。当找不到允许路的时候,我们需要修改某些点的 d 值。
实现中我们用一个 DFS 实现这个寻找最短增广的过程,如果无法继续向前,那么我们就修改当前结点的距离标号。

SAP算法有两个很好的优化GAP优化和当前弧优化。
Gap优化:我们可以注意到由于残留网络的修改只会使d(u)越来越大,所以说 d 函数是单调递增的,这就提示我们,如果d 函数出现了“断层”,就不会找到增广路径。就可以直接结束了。
当前弧优化: 可以注意到一个事实:如果说在某次迭代中从 i 出发的弧(u,v)不是允许弧,则在顶点 i 的标号 (u,v) 修改之前都不可能是允许弧。(因为 (u,v) 不变, d(v) 单调不降且 d(u)<d(v)+1 )这样,在查找允许弧的时候只需要从上一次找到的允许弧开始找。所以我们增加“当前弧”这个数据结构,记录当前顶点找到的允许弧,只有在修改这个顶点标号时才会更改这个顶点的当前弧。

下面附上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 种食物和D种饮料,每种食物或饮料只能供一头牛享用,且每头牛只享用一种食物和一种饮料。现在有 N 头牛,每头牛都有自己喜欢的食物种类列表和饮料种类列表,问最多能使几头牛同时享用到自己喜欢的食物和饮料。1<=F<=100,1<=D<=100,1<=N<=100

【建模方法】
此题的建模方法比较有开创性。以往一般都是左边一个点集表示供应并与源相连,右边一个点集表示需求并与汇相连。现在不同了,供应有两种资源,需求仍只有一个群体,怎么办?其实只要仔细思考一下最大流的建模原理,此题的构图也不是那么难想。最大流的正确性依赖于它的每一条 st 流都与一种实际方案一一对应。那么此题也需要用s-t流将一头牛和它喜欢的食物和饮料“串”起来,而食物和饮料之间没有直接的关系,自然就想到把牛放在中间,两边是食物和饮料,由 s,t 将它们串起来构成一种分配方案。至此建模的方法也就很明显了:每种食物 i 作为一个点并连边(s,i,1),每种饮料j 作为一个点并连边 (j,t,1) ,将每头牛 k 拆成两个点k,k并连边 (k,k,1),(i,k,1),(k,j,1) ,其中i, j 均是牛k喜欢的食物或饮料。求一次最大流即为结果。

本文还给读者提供一道题。

WOJ1124FootballCoach

【题目大意】
N 支球队,互相之间已经进行了一些比赛,还剩下M场没有比。现在给出各支球队目前的总分以及还剩下哪 M 场没有比,问能否合理安排这M场比赛的结果,使得第N支球队最后的总分大于其他任何一支球队的总分。已知每场比赛胜者得2分,败者0分,平局则各得1分. 1<=N<=100,0<=M<=1000

【建模方法】
首先,所有跟球队 N 相关的比赛都要让球队N赢。如果此时仍有某支球队的总分大于等于球队 N 的总分,则已经不可能满足要求;否则按如下方法建图:每场比赛i(不包括与球队 N 相关的比赛)作为一个点并加边(s,i,2),每支球队 j (不包括球队N)作为一个点并加边 (j,t,score[N]score[j]1) ,每场比赛向与其关联的两支球队 u,v 连边 (i,u,2),(i,v,2) 。若最大流等于2*比赛场数(不包括与球队 N 相关的比赛)则可以满足要求。

【本题拓展】
如果不允许平局存在,该如何构图?(s,i,2)(s,i,1),(j,t,score[N]score[j]1)(j,t,floor((score[N]score[j]1)/2)),(i,u,2),(i,v,2)(i,u,1),(i,v,1)

SPOJ962IntergalacticMap

【题目大意】
在一个无向图中,一个人要从 A 点赶往B点,之后再赶往 C 点,且要求中途不能多次经过同一个点。问是否存在这样的路线。3<=N<=30011,1<=M<=50011

【建模方法】
由于每个点只能走一次,似乎最短路之类的算法不能用,只有往网络流上靠。将每个点 i 拆成两个点(i,i′′)并加边 (i,i′′,1) 就能轻易达到这个目的。起初我一直以 A 为源点思考,却怎么也想不出如何处理先后经过两个汇点的问题,直到灵光一现,想到可以以B为源点, AC 为汇点,看能否增广两次。

最小割

最大点权独立集问题
《HOJ 2713 Matrix1》

【题目大意】
一个N*M的网格,每个单元都有一块价值Cij的宝石。问最多能取多少价值的宝石且任意两块宝石不相邻。 1<=N,M<=50,0<=Cij<=40000
【建模方法】
经典的最大点权独立集问题。转化为最小点权覆盖集:先将网格黑白染色,从源点到每个黑点有一条边,从每个白点到汇点有一条边,容量均为相应宝石的价值。每个黑点向与其相邻的四个白点连边,容量为 。设最小割为ans,结果即为 Ci,jans

《POJ 1815 Friendship》

【题目大意】
现代社会人们都靠电话通信。 AB 能通信当且仅当 AB 的电话号或者 AC 的电话号且 CB 能通信。若 AB 的电话号,那么 BA 的电话号。然而不好的事情总是会发生在某些人身上,比如他的电话本丢了,同时他又换了电话号,导致他跟所有人失去了联系。现在给定 N 个人之间的通信关系以及特定的两个人ST,问最少几个人发生不好的事情可以导致 ST 无法通信并输出这些人。如果存在多组解,输出字典序最小的一组。 2N200

【建模方法】
1:把除了 st 外其他所有点连一条弧 (i,i,1) (这条弧表示i没有丢失自己的联系人,没有这条弧,说明 i 不能喝他的朋友联系了。)
2:如果is的号码,则连一条弧 s,i, ,如果 it 的号码,则连一条弧 i,t,
3:如果 ij 的号码 ,则连弧 ij ji
求一个最小割,然后从 1n 枚举删点,若删除该点后做的最小割不变则证明这个点不在答案里。反之则要加入答案内。

《ZOJ 2532 Internship》

【题目大意】
N 个城市,ML(u,v,c)uvcCIA CIA 1N+M100,1L1000

【建模方法】
此题要求找出这样一条边,增加它的容量可以导致最大流的增加。最直观的做法是先求一次最大流记为 ans ,然后枚举每条边的容量加1再求最大流,看是否大于 ans 。但是这样做复杂度太高。不妨换种思路,假设现在满流了,我们会考虑增加哪些边的容量呢?显然是满流的边。那么增加了这条边的容量之后,总流量在什么条件下会增加呢?产生一条新的增广路.搞清楚上面的问题之后就会得到一个复杂度较好的算法,枚举做完最大流之后的每条边 uv ,如果这条边满流,并且存在 Su 的增广路以及 vT 的增广路,那么增加这条边的容量就会增大总流量。判断 Su 有无增广路可以先用dfs预处理一下,从 S 点开始只沿非满流的边走,能够到达某个位置就说明S到这个位置存在增广路。至于判断 vT 有无增广路也是类似的。并用 fromi,toi 标记点 i 能否从st。扫描每一条正向弧 (u,v) ,若它残留容量为 0AndfromuAndtov ,则 (u,v) 为一个解。

最小费用流

《HOJ 2715 Matrix3》

【题目大意】
一个 NN 的网格,每个单元都有一个价值 Vi 的宝物和一个高度 Hi 。现在ZhouGuyue要作至多K次旅行,每次旅行如下:他可以借助bin3的直升机飞到任意一个单元,之后他每次只能向相邻的且高度比当前所在格子低的格子移动。
当他移动到一个边界的格子上时,他可以跳出这个网格并完成一次旅行。旅行中所到之处的宝物他可以全部拿走,一旦拿走原来的格子里就没有宝物了。问他最多能拿走价值多少的宝物。 1<=N<=50,0<=K<=50,0<=Vi<=10000
【建模方法】
ii,i′′(i,i′′,1,Vi),(i,i′′,,0),(s,i,,0)jHi>Hj(i′′,j,,0)i(i′′,t,,0)
限制增广次数小于等于 K 求最小费用流即可。

《剪刀石头布》

【题目大意】
竞赛图中一些边已经给定,另一些边需要你去定向。问合理安排这些边的方向后,长为3的环最多能有多少(一个长为3的环叫做“剪刀石头布”现象)。3N100
【建模方法】
此题运用了“补集转化思想”。考虑集合 a,b,c 不构成剪刀石头布的情况,那么三个点中一个入度为2,一个出度为2,一个入度出度均为1。不妨以入度为2的点作思考,那么总的非剪刀石头布的情况数便为∑ ((indegree[i]2) ,剪刀石头布的情况数便为 S=(N3)((indegree[i]2)=N(N1)(N2)/6M/2(indegree[i]2)/2 。注意到 N(N1)(N2)/6M/2 是常数,那么要想让S最大,只要 (indegree[i]2)/2 最小就可以了。这里产生了凸费用函数 f(x)=x2 ,如何转化为最小费用流模型呢?每条待定向的边 i 作为一个点并加边(s,i,1,0);每个点j 作为一个点并加边 (j,t,1,1),(j,t,1,3),(j,t,1,5), 边数上界视情况而定,但不会超过 N1 条;每条边 i 向其两个顶点 u,v 连边 (i,u,1,0) 。求一次最小费用流即可。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值