网络流
一、概念
网络流用于解决流量问题
网络流:所有弧上流量的集合f={f(u,v)},称为该容量网络的一个网络流。
1、定义:带权的有向图G=(V,E),满足以下条件,则称为网络流图(flow network):
- 仅有一个入度为0的顶点s,称s为源点。
- 仅有一个出度为0的顶点t,称t为汇点。
- 每条边的权值都为非负数,称为该边的容量,记作c(i,j)。
2、弧的流量:通过容量网络G中每条弧< u,v>,上的实际流量(简称流量),记为f(u,v)。
3、可行流:在容量网络G中满足以下条件的网络流f,称为可行流。
a.弧流量限制条件: 0<=f(u,v)<=c(u,v); b:平衡条件:即流入一个点的流量要等于流出这个点的流量,(源点和汇点除外).
4、零流 若网络流上每条弧上的流量都为0,则该网络流称为零流。
5、伪流:如果一个网络流只满足弧流量限制条件,不满足平衡条件,则这种网络流为伪流,或称为容量可行流.(预流推进算法有用)。
6、对于网络流图G,流量最大的可行流f,称为最大流。
7、弧的类型:
- a.饱和弧:即f(u,v)=c(u,v);
- b.非饱和弧:即f(u,v) < c(u,v);
- c.零流弧:即f(u,v)=0;
- d.非零流弧:即f(u,v)>0.
8、链:
在容量网络中,称顶点序列(u1,u2,u3,u4,..,un,v)为一条链要求相邻的两个顶点之间有一条弧.
设P是G中一条从Vs到Vt的链,约定从Vs指向Vt的方向为正方向.在链中并不要求所有的弧的方向都与链的方向相同.
性质
1、对于任意一个时刻,设f(u,v)实际流量,则整个图G的流网络满足3个性质:
- 容量限制:对任意u,v∈V,f(u,v)≤c(u,v)。
- 反对称性:对任意u,v∈V,f(u,v) = -f(v,u)。从u到v的流量一定是从v到u的流量的相反值。
- 流守恒性:对任意u,若u不为S或T,一定有∑f(u,v)=0,(u,v)∈E。即u到相邻节点的流量之和为0,因为流入u的流量和u点流出的流量相等,u点本身不会"制造"和"消耗"流量。
2、最大流最小割定理:一个图的最大流等于其最小割。(割掉每条可行流上的最小流量边)。
Dinic算法:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<cstdlib> const int inf=0x7fffffff; using namespace std; int n,m,S,T; int a,b,c; int head[10008],vis[10008],dis[10008],num_edge=1; int deep[10008]; struct Edge{ int next,to,dis; }edge[100008<<1]; queue <int> q; void addedge(int from,int to,int dis) { edge[++num_edge].next=head[from]; edge[num_edge].to=to; edge[num_edge].dis=dis; head[from]=num_edge; } bool bfs() { memset(deep,0,sizeof(deep)); while(!q.empty()) q.pop(); deep[S]=1; q.push(S); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(deep[v]==0&&edge[i].dis) { deep[v]=deep[u]+1; q.push(v); } } } if(deep[T]==0) return 0; else return 1; } int dfs(int u,int dist) { if(u==T) return dist; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(deep[v]==deep[u]+1&&edge[i].dis!=0) { int di=dfs(v,min(dist,edge[i].dis)); if(di>0) { edge[i].dis-=di; edge[i^1].dis+=di; return di; } } } return 0; } int Dinic() { int ans=0; while(bfs()) { while(int di=dfs(S,inf)) { ans+=di; } } return ans; } int main() { scanf("%d %d %d %d",&n,&m,&S,&T); for(int i=1;i<=m;++i) { scanf("%d %d %d",&a,&b,&c); addedge(a,b,c); addedge(b,a,c); } printf("%d",Dinic()); return 0; }