网络流学习记录
总述:
目前已学习的内容:
Ford-Fulkerson O(FE)
Edmonds-Karp O(E2V)
Dinic O(EV2)
持续学习中…
Ford-Fulkerson
最大流的一种最基础的算法,想要搞清楚首先需要弄清楚反向边的作用。
假设我们已经获得了一条按如图所示流向的最大流(黑色路径),此时我们该如何调整?我们发现,此时蓝色路径(通过一条反向边)中可以再添加流量,于是这些流量就可以加到答案里,直到无法再添加流量。这就是算法的运行过程。
然后,我们来考虑为什么这样是对的。通过反向边添加流量,就相当于把原来流经这条路径的流给推了回去。观察这幅图,我们推回去的流顺着蓝色箭头向汇点传输(这也就是多出来的流),而下方蓝色箭头的流则顺着原来的黑色路径传输,没有问题。这就是反向边的作用:用来撤销我们之前有可能错误的决策。
于是这就有一个很显然的算法,一直dfs,直到无法添加流量,这也就是Ford-Fulkerson算法的原理。
#define mp(a,b,c) (Edge){a,b,c}
#define ll long long
#define INF 2000000005
using namespace std;
struct Edge{int to,c,rev;};
vector<Edge>G[M];
struct Ford_Fulkerson{
bool mark[M];
void Addedge(int s,int t,int c){
G[s].push_back(mp(t,c,G[t].size()));
G[t].push_back(mp(s,0,G[s].size()-1));
}
int dfs(int s,int t,int f){
if(s==t)return f;
mark[s]=true;
for(int i=0;i<G[s].size();i++){
int to=G[s][i].to;
if(!mark[to]&&G[s][i].c>0){
int d=dfs(to,t,min(f,G[s][i].c));
if(d>0){
G[s][i].c-=d;
G[to][G[s][i].rev].c+=d;
return d;
}
}
}
return 0;
}
int maxflow(int s,int t){
int flow=0;
while(1){
memset(mark,0,sizeof(mark));
int f=dfs(s,t,INF);
if(f==0)return flow;
flow+=f;
}
}
}Ford_Fulkerson;
Edmonds-Karp
然而dfs比较慢,所以用bfs,这就是EK算法,本质上与FF是差不多的,但是快很多(说起来好像有多快一样)。
struct Node{int to,id;}Pre[M];
int EdmondsKarp(int s,int t){
int maxflow=0;
while(1){
queue<int>Q;
for(int i=1;i<=m;i++)
Pre[i].to=Pre[i].id=0;
Q.push(s);
int f=INF;
while(!Q.empty()){
int top=Q.front();Q.pop();
for(int i=0;i<G[top].size();i++){
int to=G[top][i].to;
if(Pre[to].to||G[top][i].c==0)continue;
Pre[to].to=top;Pre[to].id=i;
if(to==t)break;
Q.push(to);
}
}
if(!Pre[t].to)return maxflow;
for(int i=t;i!=s;i=Pre[i].to)
if(G[Pre[i].to][Pre[i].id].c<f)f=G[Pre[i].to][Pre[i].id].c;
for(int i=t;i!=s;i=Pre[i].to){
G[Pre[i].to][Pre[i].id].c-=f;
G[i][G[Pre[i].to][Pre[i].id].rev].c+=f;
}
maxflow+=f;
}
}
Dinic
然后是重要的Dinic算法。它是对于EK算法的优化。为了防止每次bfs多次走经不可能的路段(向队列中加入无法到达的点进行拓展),它采用bfs+dfs分层图的设计。先是bfs找出一条能增广的路径,按此路径将各点分层。再使用dfs按照层次顺序寻找增广路径,进行增广。如果不能找到,则重新bfs构建分层图,直到bfs找不到一条路径。这种算法有效地避免了重复的搜索,降低了复杂度,是一种较为高效的最大流算法。
struct Dinic{
bool mark[M];
int id[M],level[M];
void BFS(int s){
queue<int>Q;
level[s]=0;
Q.push(s);
while(!Q.empty()){
int top=Q.front();Q.pop();
for(int i=0;i<G[top].size();i++){
int to=G[top][i].to;
if(G[top][i].c==0||level[to]!=-1)continue;
level[to]=level[top]+1;
Q.push(to);
}
}
}
int dfs(int s,int t,int f){
if(s==t)return f;
for(int &i=id[s];i<G[s].size();i++){
int to=G[s][i].to;
if(level[to]>level[s]&&G[s][i].c){
int d=dfs(to,t,min(f,G[s][i].c));
if(d){
G[s][i].c-=d;
G[to][G[s][i].rev].c+=d;
return d;
}
}
}
return 0;
}
int maxflow(int s,int t){
int maxflow=0;
while(1){
memset(level,-1,sizeof(level));
memset(id,0,sizeof(id));
BFS(s);
if(level[t]==-1)return maxflow;
int f;
while(f=dfs(s,t,INF))maxflow+=f;
}
}
}Dinic;