文章目录
- 1.网络最大流
- 题目描述
- 解析
- 反悔边
- 分层(避免环流)
- 时间优化
- 代码
- 2.费用流
- 描述
- 解析
- 代码
1.网络最大流
洛谷P3376
题目描述
给出一个网络图,以及其源点和汇点,求出其网络最大流。
解析
网络流的思想就是在原有的基础上不断进行增广
基于一个贪心的思路,先bfs判断是否存在增广路,再通过dfs增广
反悔边
但显然贪心会出错(比如当前终点通过其他点来使用会更优时)
所以就引入了反悔边
就是与所给边方向相反,一开始容量为0
增广使用边时除了把改变的边容量减去流量,再把反向边加上同样的流量即可
这样以后在必要时,就可以通过走反悔边的方式撤销之前的错误决策
举个例子
从S到T
如果贪心的走。先让2的10给了4
但实际上应该是2给5,3给4最优
那么我们就在2给4时使4到2的边容量从0加到10
这样在计算3这个点时就会顺着1-3-4-2-5-T走到终点
2-4与4-2流量都为10,等价于把一开始2到4这步操作撤销了
分层(避免环流)
为了避免dfs环流死循环的情况,我们要把这个图先分一下层
比如上图就是:
1层:S
2层:2 3
3层:4 5
4层 T
强制让dfs只能走到层数+1的点
时间优化
只是这么写的话会T掉一个点(也可能是我太菜常数太大。。。 )
考虑能否优化
我们发现,每次bfs之后的多次dfs增广中,这个图的分层结构是固定的
每个点尝试走的出边可能有一些在之前几次dfs已经用过,再枚举时其实已经得不到更多的流了
所以我们每次dfs时到每一个点就从上次dfs枚举到的出边开始枚举就行了
但注意,重新bfs后,图的结构改变,之前没用的边可能又能有流了
所以要重新从头枚举
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=300;
const int M=15500;struct node{int to,nxt;ll cap;
}p[M];
int fi[N],cnt=-1;
void addline(int x,int y,ll v){p[++cnt]=(node){y,fi[x],v};fi[x]=cnt;
}int n,m,s,t;
int a,b,c,d;int dis[N];
int cur[N];
void print(){for(int i=1;i<=n;i++) printf("%d ",dis[i]);printf("\n");}
int bfs(){queue<int>q;q.push(s);memset(dis,0,sizeof(dis));dis[s]=1;while (!q.empty()){int x = q.front();q.pop();for (int i = cur[x]= fi[x];~i;i = p[i].nxt){int to = p[i].to;if (dis[to]||!p[i].cap) continue;dis[to] = dis[x] + 1;q.push(to);}}return dis[t];
}
ll dfs(int x,ll lim){if(x==t||!lim) return lim;
// printf("%d\n",x);ll res=0;for(int &i=cur[x];~i&&lim;i=p[i].nxt){int to=p[i].to;if(dis[to]!=dis[x]+1) continue;ll f=dfs(to,min(p[i].cap,lim));lim-=f;res+=f;p[i].cap-=f;p[i^1].cap+=f;}return res;
}
ll dinic(){ll ans=0,flow;while(bfs()){while(flow=dfs(s,2e15)) ans+=flow;}return ans;
}
int main(){memset(fi,-1,sizeof(fi));scanf("%d%d",&m,&n);s=1;t=n;for(int i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);addline(a,b,c);addline(b,a,0);}printf("%lld",dinic());
}
/*
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
*/
2.费用流
洛谷P3381
描述
和最大流类似
只是每条边加了一个单位流的费用
求在最大流的前提下的最小费用方案
解析
上一题搞完这题就好多了
由于要求最小费用,把增广的bfs改为spfa跑费用的最短路
更新时记录路径
寻找路径上流量最小的值
然后累加费用即可
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5500;
const int M=55000;struct node{int to,nxt;ll cap,v;
}p[M<<1];
int fi[N],cnt=-1;
void addline(int x,int y,ll w,ll v){p[++cnt]=(node){y,fi[x],w,v};fi[x]=cnt;
}int n,m,s,t;
int a,b,c,d;int dis[N];
int pre[N],from[N];
int vis[N];
bool spfa(){pre[t]=0;memset(dis,63,sizeof(dis));memset(vis,0,sizeof(vis));queue<int>q;q.push(s);vis[s]=1;dis[s]=0;while(!q.empty()){int now=q.front();q.pop();vis[now]=0;
// printf("now=%d");for(int i=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(p[i].cap==0) continue;if(dis[to]>dis[now]+p[i].v){dis[to]=dis[now]+p[i].v;pre[to]=now;from[to]=i;if(!vis[to]){vis[to]=1;q.push(to);}}}}return pre[t];
}
void dinic(){ll flow=0,v=0,tmp;while(spfa()){tmp=2e15;for(int i=t;i!=s;i=pre[i]) tmp=min(tmp,p[from[i]].cap);flow+=tmp;for(int i=t;i!=s;i=pre[i]){v+=tmp*p[from[i]].v;p[from[i]].cap-=tmp;p[from[i]^1].cap+=tmp;}}printf("%lld %lld",flow,v);return;
}
int main(){memset(fi,-1,sizeof(fi));scanf("%d%d%d%d",&n,&m,&s,&t);for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a,&b,&c,&d);addline(a,b,c,d);addline(b,a,0,-d);}dinic();return 0;
}
/*
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
*/