网络流啊,好难啊QwQ……
看了又看,想了又想,终于会一些基础的了………
推荐一片博客,写的还是比较好懂吧戳我有喜(@ο@) 哇~
定义
网络流其实就是一个图,可以想象成输油管组成的网络,这个图有以下些基本东西。
1.每条边有两个值,容量与流量,表示这条边最大能通过的油的流量与当前已经通过的油的流量,分别用c[i,j]与f[i,j]表示。显然流量总是小于等于容量。
2.有一个点叫源点,用S表示,这个点是所有油的起点,只出油不进油。
3.有一个点叫汇点,用T表示,与源点相反。
4.流量平衡:除了ST两个点外,其他点的流入量等于流出量。
5.容量限制:流量总是小于等于容量。
6.反对称性:f[i,j]=-f[j,i]。
这就是一个网络,关于这一类的问题都是叫网络流问题吧。
基础之最大流
首先要知道,如果从一个大管道流向小管道,那么里面的流量一定是从大管道的当前流量变成小管道的容量或流量。如1=>2能流3单位,2=>3能流2单位,那么1=>3能流2单位流量。
可行流:一跳由S经过一系列点后最终流向T点,这一组流量所流过的每一条边都没有超过其容量限制,那么这就是一条可行流。
残量网络:为了程序的方便与美观(我认为的……..),定义每条边有一个r[i,j]=c[i,j]-f[i,j]。
增广路:设当前由S流向T的流量为w,那么如果再找到一条可行流,那么这条可行流便称为增广路,这次增广路流得到的量为d,那么流量w=w+d。
最大流自然就是从S到T的最大总流量。
最大流定理:当我们找不到一条增广路时,当前的流量就是最大流。
求解方法
Ford-Fulkerson方法:就是每一次寻找一条增广路进行增广,直到我们找不到增广路为止。
为什么叫方法,这是因为这只是思路啊,实现方法有很多。
著名的就是EK算法,SAP+Gap(优化),dinic,预流推进法等。
EdmondKarp算法:每次用bfs从源点寻找一条增广路,得到此次流量计入答案。若此次增广路增广的流量为0,那么当前答案即为最大流。
题目为usaco的草地排水。
dfs版
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=205;
int f[maxn][maxn],n,m,sg[maxn],num,ans;
int maxflow(int x,int now)
{
sg[x]=num;
if (x==m) return now;
fo(i,1,m)
if (sg[i]<num&&f[x][i]){
int minn=min(now,f[x][i]);
int get=maxflow(i,minn);
if (get) {
f[x][i]-=get;
f[i][x]+=get;
return get;
}
}
return 0;
}
int main()
{
freopen("T.in","r",stdin);
scanf("%d%d",&n,&m);
fo(i,1,n) {
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
f[x][y]+=val;
}
int prs=0;
do{
++num;
prs=maxflow(1,0x7fffffff);
ans+=prs;
}while (prs);
printf("%d",ans);
}
bfs版
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=205;
int f[maxn][maxn],pre[maxn],fre[maxn],sg[maxn],get[maxn];
int n,m,ans,flow,id;
bool bz[maxn];
void change(int x,int val)
{
while (x!=pre[x]){
f[pre[x]][x]-=val;
f[x][pre[x]]+=val;
x=pre[x];
}
}
int edk(int s)
{
int h=0,t=1,now=0x7fffffff;
memset(get,0,sizeof(get));
fre[1]=s; pre[1]=1; get[1]=now; bz[1]=true;
while (h!=t){
int x=fre[++h];
if (x==m) break;
sg[x]=id;
fo(i,1,m)
if (sg[i]<id&&f[x][i]) {
if (!bz[i]) fre[++t]=i,bz[i]=true;
get[i]=min(get[x],f[x][i]);
pre[i]=x;
}
}
if (get[m]) {
change(m,get[m]);
return get[m];
}
else return 0;
}
int main()
{
scanf("%d%d",&n,&m);
fo(i,1,n){
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
f[x][y]+=val;
}
do{
++id;
memset(pre,0,sizeof(pre));
memset(bz,0,sizeof(bz));
flow=edk(1);
ans+=flow;
}while (flow);
printf("%d",ans);
}
其实bfs是要快一点的。
时间复杂度为
O(nm2)
反向弧:由于增广路需要一定的顺序,才能得到最大值。但我们搜索时并不是按照顺序,所以可能得不到最大值。
我们在确定一条流量f[i,j]时,就要将f[i,j]-x,同时要给f[j,i]+x。
至于为什么是对的就不说了,去看看其他的证明吧。
反向弧是给计算机计算时一种纠正先前反向的错误的方法。
初入网络流学习,果然还是太菜了…..