【NOIp复习】网络流笔记
基础
流网络性质
容量限制:对所有的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;
}
};