最大流:源点到汇点的流量最大
Dinic基本思想:
- bfs广搜实现查找多条增广路(可能可以增加流量的路),构建一张层次图。
- 在bfs找到增广路的前提下多次dfs深搜进行增广直至所有已查找到的增广路用完
优化:当前弧优化:
在每次更新完的层次图中(即每一次bfs完后),dfs每增广完一条路之后,该路的价值就已可以看作用尽了,没有必要在之后的dfs中再次深搜该路。故记录下用完价值的边的下一条边,下一次的dfs直接从该边开始,这样就避免了不必要的增广。
/*
普通图的情况下:复杂度O(V2 E)
二分图下的复杂度:O(根号(VE))
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxv = 10004, maxe = 200004;
/*cap 为边的容量 */
struct Edge {
int next, to, cap;
}e[maxe];
int head[maxv], cnte = 1;
int level[maxv], cur[maxv];
int n, m;
inline bool min(const int& a, const int& b) { return a<b? a:b; }
inline void add_edge(int from, int to, int cap) {
cnte += 1;
e[cnte].next = head[from], e[cnte].to = to, e[cnte].cap = cap;
head[from] = cnte;
//反向边, 初始的容量为0
cnte += 1;
e[cnte].next = head[to], e[cnte].to = from, e[cnte].cap = 0;
head[to] = cnte;
return;
}
//bfs分层找增广路,建立层次图
bool bfs(int s, int t) {
memset(level, 0, sizeof(level));
queue<int> q; while(q.size()) q.pop();
level[s] = 1;
q.push(s);
while(q.size())
{
int now = q.front(); q.pop();
for(int i=head[now]; i; i=e[i].next)
{
//有容量且没有被标记
if(level[e[i].to]==0 && e[i].cap > 0) {
level[e[i].to] = level[now]+1;
q.push(e[i].to);
}
}
}
//终点的level大于0说明存在增广路
return level[t] > 0;
}
//增广流量
//in 为源点输入到这个点上的最小边权
//now 收到的支持,不一定真正能够用到
//@return 真正的输出流量
/** dfs的意义为到达now时传进的最大流量 */
int dfs(int now, int t, int in) {
if(!in || now==t)
return in;
//i为cur[now]的引用-->cur[now]随着i改变
for(int& i=cur[now]; i; i=e[i].next) {
int nv = e[i].to;
if(level[nv]==level[now]+1 && e[i].cap>0)
{
// out为点now经边i流向nv的流量
int out = dfs(nv, t, min(in, e[i].cap)); //一路上都受到最小流量的限制
if(out > 0) {
e[i].cap -= out;
e[i^1].cap += out; //反向边,用于反悔
return out;
}
}
}
return 0; //没有增广路,返回0
}
//注意是两个while,每次bfs找到多个增广路后
//多次dfs计算网络中所有增广路的流量
int dinic(int s, int t) {
int max_flow = 0;
while(bfs(s, t))
{
//每次建立完层次图之后记录当前弧
for(int i=1; i<=n; ++i)
cur[i] = head[i];
int tmp=0;
while(tmp = dfs(s, t, 0x3f3f3f3f))
max_flow += tmp;
}
return max_flow;
}