图论之最大流问题(三)
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; }
以上是关于图论之最大流问题(三)的主要内容,如果未能解决你的问题,请参考以下文章