Ford-Fulkerson
起点是s,终点是t
残余网络:满足f(e)<c(e)的边和f(e)>0的反向边(这个边是一开始就可以加上去,设为0)这是一个寻找增广路的工具,
这里涉及到一个退流的概念,如果找到一条可以更新的路径,那么可能需要把原来的某段流退回去一部分(当然也有可能完全不退)
增广路:利用残余网络找到的s-t路径
我们可以用最大流最小割定理间接证明这个算法是正确的。
算法实现:不断对残余网络进行搜索,知道找不到增广路为止
算法复杂度为o(FV),实际应该比估算要快一点
当顶点数较多或者流的总量较大是这个算法偏慢。
int dfs(int v,int t,int f)
{
if(v==t) return f;
use[v]=1;
for(int i=0;i<tu[v].size();i++)
{
node &e=tu[v][i];
if(!use[e.to]&&e.cap>0)
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
tu[e.to][e.wei].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
int flow=0;
for(;;)
{
memset(use,0,sizeof(use));
int f=dfs(s,t,1e8);
if(f==0) return flow;
flow+=f;
}
}
Dinic
这个算法比上述的要快。
这里引入一个分层图的概念,不断得寻找最短增广路,可以得出每跑完一个分层图之后最短增广路都会至少加一。
why?在当前分层图无法在找到增广路的情况下,要么就是不存在增广路了,要么就是最短增广路变长了(不可能变短了)
多次考虑当前从起点到终点的最短路径,就多次bfs,对于每次的bfs找最短路,对于任何一条最短路我们寻找增广路,由于最短路的长度不会超过n,
如果再加入一个当前弧优化(也就是避免对无用边的多次检查),最后的复杂度不会超过上界O(nm^2) 这样对于一些最大流上界较大的题就比fulkerson快了很多。
提到当前弧优化其实不得不提一下EK算法,就是每次bfs寻找一个增量,路径中流量严格小于容量的增量,作为一条增光路,由于是bfs所以需要记录一下路径的轨迹。
Dinic相对于EK的优化在于,同一增光路长度的增光路一次性全找出来,然后可以忽略掉无用边的遍历。
总的来说,Ford-Fulkerson就是废物,连EK都比它快,初学者其实没必要学前者,至于Dinic就更不用说了。三者的核心都是增光路定理。
而且,实际上应用中速度会非常快,由于上界比较松的原因。
这里给一发dinic的板子:
void jian(int s,int t,long long val)//这里是jian图
{
// cout<<"val="<<val<<endl;
a[s].push_back(node{t,val,a[t].size()});
a[t].push_back(node{s,0,a[s].size()-1});
}
long long dfs(int s,int t,long long f)
{
// cout<<"f="<<f<<endl;
// cout<<s<<" "<<t<<endl;
if(s==t)
{
// cout<<"f="<<f<<endl;
return f;
}
// use[s]=1;
int tt=a[s].size();
// cout<<tt<<endl;
for(int &i=iter[s];i<tt;i++)//当前弧优化
{
node &e=a[s][i];
// cout<<e.to<<" "<<e.val<<endl;
if(level[e.to]>level[s] && e.val>0)
{
// cout<<"fsjldf"<<endl;
long long d=dfs(e.to,t,min(f,e.val));
// cout<<"d="<<d<<endl;
if(d>0)
{
e.val-=d;
a[e.to][e.fan].val+=d;
return d;
}
}
}
return 0;
}
void bfs(int s)
{
memset(level,-1,sizeof(level));
queue<int> que;
level[s]=0;
que.push(s);
while(!que.empty())
{
int v=que.front();que.pop();
for(int i=0;i<a[v].size();i++)
{
node &e=a[v][i];
if(e.val>0 && level[e.to]<0)
{
level[e.to]=level[v]+1;
que.push(e.to);
}
}
}
}
long long daliu(int s,int t)//显然是最大流的意思
{
long long flow=0;
while(1)
{
bfs(s);
// cout<<level[t]<<endl;
if(level[t]<0) return flow;
memset(iter,0,sizeof(iter));
long long f;
// cout<<"flow="<<flow<<endl;
while((f=dfs(s,t,inf))>0)
{
// cout<<"f="<<f<<endl;
flow+=f;
// cout<<"flow="<<flow<<endl;
}
}
}