最大流学习笔记
Part1:最大流入门
概念
源点:所有流量都从源点出来;
汇点:所有流量最终都流入汇点;
最大流:对于一张图,从源点流向汇点的最大流量
增广路:找到一条路径使得总流量增加
残余网络:每搜一次相应建立“反悔机制”,即将路径上用掉的边建一条反向边,流量和原边相同,建完后的图称为残余网络
结论
- 定义w(x,y)为从x到y的流量限制,flow(x,y)表示实际从x到y的流量
- flow(x,y)<=w(x,y)
- flow(x,y)=-flow(y,x)
- ∑ i f l o w ( x , i ) = ∑ j f l o w ( j , x ) \sum^{}_{i}{flow(x,i)} = \sum^{}_{j}{flow(j,x)} ∑iflow(x,i)=∑jflow(j,x)
Ford-Fulkerson算法
key:dfs找增广路,建立残余网络,不停地在残余网络上找增广路,直到找不到任何一条增广路为止
时间复杂度: O ( C × n 2 2 ) O(C\times \frac{n^2}{2}) O(C×2n2)
C:最大规模容量,N:点数
注:Ford-Fulkerson算法并非可实现算法
Edmond-Karp Algorithm
Key: dfs–>bfs找一条最短路径
在Ford-Fulkerson算法的基础上,将dfs找增广路径的策略替换成用bfs找一条s到t的最短路径
复杂度: O ( N M 2 ) O(NM^2) O(NM2)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
const int inf=0x3f3f3f3f;
int n,m,s,t,tot=1,maxflow;
int head[maxn],pre[maxn],increase[maxn],vis[maxn];
struct node
{
int from,to,next;
int val;
}edge[maxn];
inline void add(int x,int y,int z)
{
edge[++tot].next=head[x];
edge[tot].from=x;
edge[tot].to=y;
edge[tot].val=z;
head[x]=tot;
}
inline int read()
{
int s=0,f=1;
char c=getchar();
while (c<'0'||c>'9')
{
if (c=='-')
{
f=-1;
}
c=getchar();
}
while (c>='0'&&c<='9')
{
s=s*10+c-48;
c=getchar();
}
return s*f;
}
bool bfs()//找到一条从s到达t的增广路,且增加的流量为increase[t]
{
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(s);
vis[s]=true;
increase[s]=inf; //最小剩余流量,源点的流量是无限的
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x];i;i=edge[i].next)
{
if (edge[i].val)
{
int y=edge[i].to;
if (vis[y]==true)
{
continue;
}
increase[y]=min(increase[x],edge[i].val);//从源点到y的最大流量限制
pre[y]=i;
q.push(y);
vis[y]=true;
if (y==t)
{
return true;
}
}
}
}
return false;
}
void update()
{
int cur=t; //反过来走到s
while (cur!=s)
{
int last=pre[cur]; //最后一条边的编号
edge[last].val-=increase[t];
edge[last^1].val+=increase[t]; //反边
cur=edge[last^1].to;//cur的上一个点
}
maxflow+=increase[t];
return ;
}
int main()
{
n=read(),m=read(),s=read(),t=read();
memset(head,0,sizeof(head));//tot=1,第一条边从2开始,对应反边需要异或
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,0);
}
while (bfs()==true)
{
update();
}
cout<<maxflow<<endl;
return 0;
}
Dinic Algorithm
EK算法缺陷:
- 盲目
- 每次bfs找一条增广路径
那么我们就想要每次bfs或者dfs时找出多条增广路
我们通过建立分层图来增加效率
每次只走向下一层
这样我们走的一定是最短路
时间复杂度: O ( N 2 M ) O(N^2M) O(N2M) //绝大多数情况Dinic比EK优秀(若点数和边数接近则复杂度差不多)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
const int inf=0x3f3f3f3f;
int n,m,s,t,tot=1,maxflow;
int head[maxn],pre[maxn],increase[maxn],vis[maxn],level[maxn];
struct node
{
int from,to,next;
int val;
}edge[maxn];
inline void add(