【NOIp复习】网络流笔记

来源:互联网 发布:国产垃圾 知乎 编辑:程序博客网 时间:2024/05/23 19:18

基础

流网络性质

  容量限制:对所有的u,v∈V,要求f(u,v)<=c(u,v)。  反对称性:对所有的u,v∈V,要求f(u,v)=-f(v,u)。  流守恒性:对所有u∈V-{s,t},要求∑f(u,v)=0 (v∈V)。

残流网络

在给定的流网络G=(V,E)中,设f为G中的一个流,并考察一对顶点u,v∈V,在不超过容量c(u,v)的条件下,从u到v之间可以压入的额外网络流量,就是(u,v)的残留容量。残留容量的定义为:cf(u,v)=c(u,v)-f(u,v)。【f指已有的流量】而由所有属于G的边的残留容量所构成的带权有向图就是G的残留网络。

性质:残流网络中的流与原流相加一定也是原流网络的一个可行流。很明显嘛…若f是G中的一个流,Gf是由G导出的残留网络,f’是Gf中的一个流,则f+f’是G中一个流,且其值|f+f’|=|f|+|f’|。

增广路径

增广路径p为残流网络Gf中从s到t的一条简单路径。根据残留网络的定义,在不违反容量限制的条件下,G中所对应的增广路径上的每条边(u,v)可以容纳从u到v的某额外正网络流。而能够在这条路径上的网络流的最大值一定是p中边的残留容量的最小值。

最大量为p的残留网络定义为:cf(p)=min{cf(u,v) | (u,v)在p上}【残流网络cf中剩余容量最小的容量即为残流网络的最大量p】

流网络G(V,E)的割(S,T)将V划分成为S和T=V-S两部分,使得s∈S,t∈T。【顶点被割分成两部分】

如果f是一个流,则穿过割(S,T)的净流被定义为f(S,T)=∑f(x,y) (x∈S,y∈T)【净流:割分为的两部分顶点之间的流量和】

割(S,T)的容量为c(S,T)。【和净流定义类似,为S->V中所有顶点对(u,v)的容量和】

一个网络的最小割就是网络中所有割中具有最小容量的割。

设f为G中的一个流,且(S,T)是G中的一个割,则通过割(S,T)的净流f(S,T)=|f|。

证明:f(X,Y)=∑f(u,v),其中u∈X,v∈Y

所以得到结论:最小割对应的流就是该流网络最大流

最大流最小割定理

  如果f是具有源s和汇点t的流网络G=(V,E)中的一个流,则下列条件是等价的:  1) f是G中一个最大流。  2) 残留网络Gf不包含增广路径。  3) 对G的某个割(S,T),有|f|=c(S,T)。

增广路算法(Edmonds-Karp算法)

struct Edge{    int from,to,cap,flow;//cap是容量,flow是流量    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}}; struct EK{    int n,m;    vector<Edge> edges;    vector<int> G[maxn];//邻接表,G[i][j]代表从i出发的第j条边的另一端点在流网络中的序号     int a[maxn];//从源点到i点的可改进量     int p[maxn];//最短路树上i的入弧编号     void init(int n){        for(int i=0;i<n;i++) G[i].clear;        edges.clear();    }//注意init在C里面是保留字,只能用来初始化哟     void addEdge(int from,int to,int cap){        edges.push_back(Edge(from,to,cap,0));        edges.push_back(Edge(to,from,cap,0));        m=edges.size();        G[from].push_back(m-2);         G[to].push_back(m-1);         /*            偶数号边是从正流;奇数号边是负流            访问一条边i的反向边访问i^1即可             (与此类似的有,在滚动数组中,用i&1访问当前状态,(i+1)&1访问上一状态)         */     }    int maxFlow(int s,int t){        int flow=0;//初始流量为0         while(true){            memset(a,0,sizeof(a));//所有点(除源点)都可能改进,赋值0             queue<int> Q;            Q.push(s);//源点入队            a[s]=INF;//源点到i点的可改进量不能再大了,赋值无穷大             while(!Q.empty()){                int cur=Q.front(); Q.pop();//cur记录出队点序号                 for(int i=0;i<G[cur].size();i++){//考察与cur相连的所有边                     Edge &e=edges[G[cur][i]];//e是与cur相连的当前考察边,设这条边为(cur,p)                     if(!a[e.to] && e.cap>e.flow){//如果p没有被改进过且e容量没有被用尽                         p[e.to]=G[cur][i];//流入p的边编号为p[p],这里是G[cur][i]这条边                         a[e.to]=min(a[cur],e.cap-e.flow);//将p点的可改进量更新为最大(如果残量足够cur全部流入,否则就占满残量)                         Q.push(e.to); //p点入队                     }                 }                if(a[t]) break; //如果改进到汇点了就gg吧             }            if(!a[t]) break;//队列为空了还没改进到汇点,说明根本流不通,可以gg了             for(int u=t;u!=s;u=edges[p[u]].from){//从汇点开始,一路跑边;p[x]存的是最大流网络中x点的入边编号                edges[p[u]].flow+=a[t];//正向边流量加上改进量                 edges[p[u]^l].flow-=a[t];//反向边流量减去改进量             }            flow+=a[t];//总流量加上改进量         }        return flow;    }};
0 0
原创粉丝点击