网络流学习笔记2
网络流(网络最大流)学习笔记2
上次写了那个网络流笔记1,是关于FF算法的。
这次来一发拓展——EK算法和Dinic算法。
I-sap 先咕咕咕算了
Emdond-Karp 算法
Edmond-Karp算法(下简称EK算法)是对FF算法的bfs优化。
原先dfs算起来有可能搜到好几次,但是广搜对于判重有着天然的优势。
所以bfs优化以后时间复杂度(\(O(VE^2)\))反而小了。
需要用到的是残量网络,即可行流流过去剩下的可以流的大小。
先放一个邻接矩阵的(Luogu【模板】网络最大流 上面MLE了三个点 70pts)
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int INF=2100000000,maxn=10001;
int left_flow[maxn][maxn];
int pre[maxn];
int flow[maxn];
int n,m;
inline int bfs(int S,int T)
{
queue<int>q;
for (register int i=1; i<=n; i++)
{
pre[i]=-1;
}
pre[S]=0;
flow[S]=INF;
q.push(S);
while (!q.empty())
{
int from=q.front();
q.pop();
if (from==T)
break;
for (register int i=1; i<=n; i++)
{
if (i!=S && left_flow[from][i]>0 && pre[i]==-1)
{
pre[i]=from;
flow[i]=min(flow[from],left_flow[from][i]);
q.push(i);
}
}
}
if (pre[T]==-1)
return -1;
else
return flow[T];
}
inline int EK(int S,int T)
{
int canflow=0,nowflow=0;
while (canflow=bfs(S,T),canflow!=-1)
{
int now=T;
while(now!=S)
{
int from=pre[now];
left_flow[from][now]-=canflow;
left_flow[now][from]+=canflow;
now=from;
}
nowflow+=canflow;
}
return nowflow;
}
int main()
{
int S,T;
scanf("%d%d%d%d",&n,&m,&S,&T);
for (register int i=1; i<=m; i++)
{
int f,t,v;
scanf("%d%d%d",&f,&t,&v);
if (f==t)
continue;
left_flow[f][t]+=v;
}
printf("%d\n",EK(S,T));
return 0;
}
因为邻接矩阵实在是效率太低啦
所以我们换成链式前向星,保证只遍历出边。
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int INF=2100000000,maxn=10001;
struct edge{
int from,to,rev,next,left_flow;
};
edge Edge[200001];
int cnt=1,head[10001];
inline void add_edge(int from,int to,int flow)
{
//正向边
Edge[cnt].from=from,
Edge[cnt].to=to,
Edge[cnt].left_flow=flow,
Edge[cnt].next=head[from],
Edge[cnt].rev=cnt+1;
head[from]=cnt++;
//反边
Edge[cnt].from=to,
Edge[cnt].to=from,
Edge[cnt].left_flow=0,
Edge[cnt].next=head[to],
Edge[cnt].rev=cnt-1;
head[to]=cnt++;
}
int pre[maxn],flow[maxn],n,m;
inline int bfs(int S,int T)
{
queue<int> q;
for (register int i=1;i<=n;i++)
pre[i]=-1;
flow[S]=INF;
pre[S]=0;
q.push(S);
while (!q.empty())
{
int from=q.front();
q.pop();
if (from==T)break;
for (register int i=head[from];i;i=Edge[i].next)
{
int to=Edge[i].to;
if (Edge[i].left_flow>0 && pre[to]==-1)
{
pre[to]=i;
flow[to]=min(flow[from],Edge[i].left_flow);
q.push(to);
}
}
}
if (pre[T]==-1)
return -1;
else return flow[T];
}
inline int EK(int S,int T)
{
int canflow=0,maxflow=0;
while (canflow=bfs(S,T),canflow!=-1)
{
int now=T;
while (now!=S)
{
Edge[pre[now]].left_flow-=canflow;
Edge[Edge[pre[now]].rev].left_flow+=canflow;
now=Edge[pre[now]].from;
}
maxflow+=canflow;
}
return maxflow;
}
int main()
{
int S,T;
scanf("%d%d%d%d",&n,&m,&S,&T);
for (register int i=1;i<=m;i++)
{
int f,t,v;
scanf("%d%d%d",&f,&t,&v);
if (f==t)continue;
add_edge(f,t,v);
}
printf("%d\n",EK(S,T));
return 0;
}
(话说我为了27行眼瞎直接调了一晚上
Dinic 算法
Magic!将dfs和bfs相结合,分层之后时间复杂度下来了。
好像在一般情况下,变成了\(O(V^2E)\),而一般\(V\leq E\),所以Dinic优化的不是一点半点。
(下一个系列再说吧QAQ