网络流例题及构图选讲

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jasonvictoryan/article/details/48324407

最大流最小割问题

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

本文介绍一种求最大流的方法。SAP 算法(最短增广路算
法)。最短增广路算法(Shortest Augmenting Path Algorithm),即每次寻找包含弧的个数最少的增广路进行增广,可以证明,此算法最多只需要进行|v||s|/2次增广。并且引入距离标号的概念,可以在的时间O(|v|)里找到一条最短增广路。
最终的时间复杂度为O(|V|2|E|),但在实践中,时间复杂度远远小于理论值(特别是加了优化之后),因此还是很实用的。
距离标号:对于每个顶点u赋予一个非负整数值d(u)来表示ut的”距离“远近,当然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的号码 ,则连弧ijji
求一个最小割,然后从1n枚举删点,若删除该点后做的最小割不变则证明这个点不在答案里。反之则要加入答案内。

《ZOJ 2532 Internship》

【题目大意】
N个城市,ML(u,v,c)uvcCIACIA1N+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)。求一次最小费用流即可。

阅读更多

没有更多推荐了,返回首页