网络流
0.2
Table of Contents
基本介绍
在一定容量在众多路径中找最大流量,思路:增广(bfs)+反向弧,代码+注解就能懂的基本操作.
简单解释反向弧,就是在发现一步操作不最优时,不撤回而是往回走一步抵消其影响.
不懂?贴两个温馨提示:
代码实现(p3376)
DINIC
最常见的算法,利用分层图思想.下面函数dinic()内嵌了一个dfs,因为dfs是dinic区别ek的点.
在普通情况下, DINIC算法时间复杂度为O(V2E)
在二分图中, DINIC算法时间复杂度为O(√VE)
//dinic
//经测试手写队列较省时空
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int n,m,s,t,ro=-1;
int first[200005],d[10005];
struct $
{
int t,c,f,ne;
}a[200005];
int q[10005],head,tail;
void road(int x,int y,int c)
{
a[++ro].t=y;
a[ro].c=c;
a[ro].ne=first[x];
first[x]=ro;
}
bool bfs()
{
memset(d,0,sizeof(d));
head=0;tail=1;
q[1]=s;
d[s]=1;
while(head<tail)
{
int x=q[++head];
for(int i=first[x];i!=-1;i=a[i].ne)
{
int t1=a[i].t;
if(a[i].c>a[i].f&&!d[t1])
{
d[t1]=d[x]+1;
q[++tail]=t1;
}
}
}
return d[t];
}
int dinic(int x,int flow)
{
if(x==t) return flow;
int ans=0;
for(int i=first[x];i!=-1&&ans<flow;i=a[i].ne)
{
int t1=a[i].t;
if(a[i].c>a[i].f&&d[t1]==d[x]+1)
{
int sum=dinic(t1,min(flow-ans,a[i].c-a[i].f));
ans+=sum;
a[i].f+=sum;
a[i^1].f-=sum;//反向弧
}
}
return ans;
}
int main()
{
scanf("%d %d %d %d\n",&n,&m,&s,&t);
memset(first,-1,sizeof(first));
for(int i=1;i<=m;i++)
{
int x,y,c;
scanf("%d %d %d\n",&x,&y,&c);
road(x,y,c);road(y,x,0);
}
int ans=0;
while(bfs())
ans+=dinic(s,99999999);
printf("%d\n",ans);
return 0;
}
EK
EK简单地说,就是通过bfs每次找一遍增广路径,算法的复杂度上限为ve^2.由于每次都要从头开始,可以处理加边的情况.
应用
二分图匹配(P3386)
将左半部分和右半部分根据题目所边连接,直接暴力跑dicinic.注意汇点t=n+m+2,特别注意反向弧编号.
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
int fi[1000003],n,ro;
struct $
{
int t,ne,c,re;
}a[1000003];
void road(int x,int y,int c,int re)
{
a[++ro].ne=fi[x];
a[ro].t=y;
a[ro].c=c;
a[ro].re=re;
fi[x]=ro;
}
int d[1000003];
int q[1000003];
bool bfs()
{
memset(d,0,sizeof(d));
int he=0,ta=1; d[1] = 1;
q[he]=1;
while(he<ta)
{
int x = q[he++];
for(int i=fi[x]; i; i=a[i].ne)
{
if(a[i].c>0 && !d[a[i].t])
{
d[a[i].t] = d[x]+1;
q[ta++]=a[i].t;
}
}
}
return d[n];
}
int dicnic(int x,int flow)
{
if(x==n) return flow;
int ans = flow;
for(int i=fi[x]; i; i=a[i].ne)
{
if(d[a[i].t]==d[x]+1 && a[i].c>0 && ans)
{
int sum = dicnic(a[i].t,min(a[i].c,ans));
if(sum>0)
{
a[i].c -= sum;
ans -= sum;
a[a[i].re].c += sum;
}
}
}
return flow-ans;
}
int main()
{
int n1,m,e;
scanf("%d%d%d",&n1,&m,&e);
n = n1+m+2;
for(int i=1; i<=e; i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(x<=n1 && y<=m)
{
road(x+1,y+n1+1,1,ro+2);
road(y+n1+1,x+1,0,ro);
}
}
for(int i=1; i<=n1; i++)
{
road(1,i+1,1,ro+2);
road(i+1,1,0,ro);
}
for(int i=1; i<=m; i++)
{
road(i+n1+1,n,1,ro+2);
road(n,i+n1+1,0,ro);
}
int ans=0;
while(bfs())
ans += dicnic(1,0x7fffffff);
printf("%d\n",ans);
return 0;
}