图论之最大流问题(三)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论之最大流问题(三)相关的知识,希望对你有一定的参考价值。

参考技术A 在前几天的文章里面,我们讲到求解最大流的关键是找到增广路,并且单独介绍了一个求增广路的Ford-Fulkerson算法,也叫做标号法。事实上还有许多别的求增广路的算法,今天我们就再介绍一个“最短增广路”算法。

最短增广路的思想其实很简单, 既然需要找一条增广路来优化,那就直接找最短的那条增广路吧 。

首先介绍两个重要概念,一个叫 残留量 :给定容量网络G(V, E)及可行流f,弧上的残留容量记为c'(u, v)= c(u, v)–f(u,v)。每条弧的残留容量表示该弧上可以增加的流量。因为,从顶点u到顶点v流量的减少,等效于顶点v到顶点u流量增加,所以每条弧上还有一个反方向的残留容量c'(v,u) =–f(u,v)。由残留量组成的网络叫做 残留网络 ,下面给个例子:

上图a中的两个数字分别表示弧的容量和已经用掉的容量。

注意b图,它里面各个点之间其实有一个层次关系,比如Vs是第0级,V1和V2是第一级,等等。下面是网络节点按级划分的结果:

对残留网络分层后,删去 比目的点Vt层次更高的顶点和与目的点Vt同层的顶点 ,然后 删去与这些顶点关联的弧 ,再 删去从某层顶点指向同层顶点和低层顶点的弧 ,所剩的各条弧的容量与残留网络中的容量相同,这样得到的网络是残留网络的子网络,称为 层次网络 。

解释一下构造层次网络的时候为什么要这么删除多余的边:删除比Vt层次更高,以及同级别的点的原因很简单:Vt是目的点, 不允许有从Vt中流出的流量 。删除指向同层以及底层的边是因为我们要找的是最短增广路, 留着这些边会把增广路边长 。有人可能会问,少考虑了一些潜在的增广路,不会对最终结果有影响么?别担心,当前的增广路被处理完之后,在下一轮循环里就不再考虑了,此时这些上一轮没有考虑到的、仍旧有剩余流量的边就会被考虑到的。

不知道大家注意到没有, 层次网络中任何一条连接源点Vs和目的点Vt的通路都是一条增广路,并且是最短的增广路 。

知道寻找最短增广路之后问题就简单了,找到之后优化它,然后重新找找看还有没,还有增广路的话继续优化,直到不存在增广路为止。

n

HDU 5652 图论之并查集

点击打开链接

题意:上边是中国,下边是印度,黑点的部分不可以走,下面的矩阵1代表黑点不能走,然后给了Q,每一次将一个点变成黑点,即不能走,问最少多少次就可以完成

思路:如果有一条黑点连成的线,从走到右的说明我们达成了目的,那就好办了,每一个黑点可以与周围的8个黑点相连,这样我们在最左边建个汇点最右边建个源点,每次询问这两个点在不在一个集合就行了,用并查集轻松实现,只要两个点相连就放到一个集合里

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=500010;
int f[maxn];
int dir[8][2]={{1,0},{-1,0},{0,1},{0,-1},{-1,-1},{-1,1},{1,-1},{1,1}};
char str[510][510];
void init(){
    for(int i=0;i<maxn;i++){
        f[i]=i;
    }
}
int find1(int x){
    if(x!=f[x])
        f[x]=find1(f[x]);
    return f[x];
}
void unite(int a,int b){
    int aa=find1(a);
    int bb=find1(b);
    if(aa==bb) return ;
    f[aa]=bb;
}
int main(){
    int T,n,m,ask,a,b;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d",&n,&m);
        int S=0,T=n*m+1;
        for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(j==1&&str[i][j]=='1') unite(S,(i-1)*m+j);
                if(j==m&&str[i][j]=='1') unite(T,(i-1)*m+j);
                if(str[i][j]=='1'){
                    for(int k=0;k<8;k++){
                        int xx=i+dir[k][0];
                        int yy=j+dir[k][1];
                        if(xx<1||xx>n||yy<1||yy>m||str[xx][yy]!='1') continue;
                        unite((xx-1)*m+yy,(i-1)*m+j);
                    }
                }
            }
        }
        int ans=-1;
        if(find1(S)==find1(T)) ans=0;
        scanf("%d",&ask);
        for(int j=1;j<=ask;j++){
            scanf("%d%d",&a,&b);
            a++;b++;
            if(ans!=-1) continue;
            str[a][b]='1';
            if(b==1) unite(S,(a-1)*m+b);
            if(b==m) unite(T,(a-1)*m+b);
            for(int i=0;i<8;i++){
                int xx=a+dir[i][0];
                int yy=b+dir[i][1];
                if(xx<1||xx>n||yy<1||yy>m||str[xx][yy]!='1') continue;
                unite((xx-1)*m+yy,(a-1)*m+b);
            }
            if(find1(S)==find1(T)) ans=j;
        }
        printf("%d\n",ans);
    }
    return 0;
}

以上是关于图论之最大流问题(三)的主要内容,如果未能解决你的问题,请参考以下文章

为啥图论中的最大流算法对于最大二分匹配是正确的

图论之二分图匹配

图论模板——最大流及费用流模板

图论算法-网络最大流EK;Dinic

图论——最大流(DINIC)

UVa563_Crimewave(网络流/最大流)(小白书图论专题)