我之前有一篇博文;
http://blog.csdn.net/largecub233/article/details/54946567
不好意思啊,这博客不是我自己写的,我也不知到是那个傻逼写的;
我自己写的博客我自己怎么会看不懂,对不对;
那博客太垃圾了我靠,我今天从新写一篇;
网络流的大意就看题目嘛
https://www.luogu.org/problem/show?pid=3376
这里的题面就是一个模型;
在自己的做题里,模型往往是要自己构造的;
网络流偶两个点
源点S
汇点T(我有时写E,就是end)
从S出发连用好多调有向边连阿连连到E
网络流有个名次叫增广路,这个你们自己查;
简单可以理解为一条路径从S到T并且可以通过这一条让最大流增大;
最大流这个你们自己看吗,我不想讲了;
上面
15-20-5-4就是增广路,因为通过这条路径可以让S到T的流量增加4;
因为路径上最小的权就是4,流过的流量不可以大于权;
当我们吧这条路增广后,这条路就变成了
11-16-1-0;
这条录对答案的贡献是4;
最大流就是经可能的找到增广路并且更新答案;
那么对于这题,我们有了第一个算法
E-K算法
我们进行多次dfs,每次dfs找到一条增广路并更新答案;
因为一单发现一条增广路,那么这一条路径上的值都要修改
对,这个就是大暴力;
原理可以看我的上一篇垃圾博客的图;
然后我们考虑优化;
我们可不可以像dp一样通过更新子节点推的父节点,一遍OK?
不可以;
因为这不是树,一个点有多个父亲;
这时我们迎来一种dinic算法;
先bfs
bfs时我们把当前图分层,就是对于每一个点维护一个deep;
后dfs
用下一层的节点跟新这一层的点,一次性获取多条增广路,一次性求解;
两者不断反复;
这时我们会发现,每次dfs,会有很多边的权值变成0;
那我们在bfs的时候,反遇到边权为0的点就不再深入;
这样不断bfs dfs,一定会遇到bfs无法更新到T节点,这时已经没有增广路,退出;
棕色就是深度,我们第一次dfs完后变成
答案就增加5
我们再次bfs
再dfs
答案增加1;
这时,ST已经不相联通了,结束;
但是问题来了,我们怎么dfs?
我们这么可以保证dfs的路径一定是正确的;
我们不能保证;
但是我们有法宝,反向边;
我们建立反向边,一开始值都是0;
当这条边流量增加x
那么这条边的权值减去x;
其反向边就加x;
反向边参与bfs和dfs,并且对答案产生贡献;
我为什么?;
我们dfs
第一次
第二次
答案只有7,其实最大流有9;
现在我们建立反向边;
左边是反向边的值
第一次
二次有点乱
ans=4
ans=4+3
第三次的时候发现中间那边的反向边值为4,所以从中间的边走
最终答案=4+3+2=9;
正确;
一开始中间的路向下走4的流量;
后来又通过反向边上去2 的流量;
等同于一开始只下来2的流量
原理就是这样,互补;
你给我的多了,现在你不够了,我通过反向边补给你;
自己画画图吧;
之后就是看代码的时间了,再看代码之前;
先感谢Fop_zz大佬的耐心教育
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
struct cs{int to,nxt,v;}a[N*20];
int head[N],ll=1;
int q[N],l,r;
int deep[N];
int n,m,S,E,x,y,z,ans;
void init(int x,int y,int z){a[++ll].to=y;a[ll].v=z;a[ll].nxt=head[x];head[x]=ll;}
bool bfs(){
memset(deep,0,sizeof deep);
q[1]=S;l=r=1;deep[S]=1;
for(;r>=l;l++){
int x=q[l]; if(x==E)return 1;
for(int k=head[x];k;k=a[k].nxt)
if(!deep[a[k].to]&&a[k].v!=0)
deep[a[k].to]=deep[x]+1,q[++r]=a[k].to;
}return 0;
}
int dfs(int x,int now){
if(x==E)return now;
int ans=0;
for(int k=head[x];k;k=a[k].nxt)
if(deep[a[k].to]-1==deep[x]&&a[k].v){
int temp=dfs(a[k].to,min(a[k].v,now));
now-=temp; ans+=temp;
a[k].v-=temp; a[k^1].v+=temp;
if(!now)return ans;
}return ans;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&S,&E);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
init(x,y,z);init(y,x,0);
}
while(bfs())ans+=dfs(S,1e9);
printf("%d",ans);
}
搜索这种东西,玄学,不好优化;
但是,优化还是有的;
当前弧优化
对于每次dfs前,加一个cur[x]
代表x点要从第几个点开始搜;
所以再一个dfs里面每个点x他的节点只会被dfs一次;
为什么这样快;
因为那你x点dfs一遍了,x下面必然出现满流,预期直接dfs,不如先放一放,等到bfs后再去dfs;
我靠我也不知到为什么这样快QAQ
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1e4+5;
struct cs{int to,nxt,v;}a[N*20];
int head[N],ll=1,cur[N];
int q[N],l,r;
int deep[N];
int n,m,S,E,x,y,z,ans;
void init(int x,int y,int z){a[++ll].to=y;a[ll].v=z;a[ll].nxt=head[x];head[x]=ll;}
bool bfs(){
memset(deep,0,sizeof deep);//这个其实可以优化的,我不会
q[1]=S;l=r=1;deep[S]=1;
for(;r>=l;l++){//当前弧优化赋值↓
int x=q[l];cur[x]=head[x];if(x==E)return 1;//发现跟就可以退出了
for(int k=head[x];k;k=a[k].nxt)
if(!deep[a[k].to]&&a[k].v!=0)
deep[a[k].to]=deep[x]+1,q[++r]=a[k].to;
}return 0;
}
int dfs(int x,int now){//now代表当前x的可用流
if(x==E)return now;
int ans=0;
for(int &k=cur[x];k;k=a[k].nxt)//只访问一次
if(deep[a[k].to]-1==deep[x]&&a[k].v){//这个优化a[k].v会快的
int temp=dfs(a[k].to,min(a[k].v,now));
now-=temp; ans+=temp;
a[k].v-=temp; a[k^1].v+=temp;//反向边
if(!now)break;//优化
}return ans;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&S,&E);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
init(x,y,z);init(y,x,0);
}
while(bfs())ans+=dfs(S,1e9);
printf("%d",ans);
}