标签:最小流 const 顶点 new 邻接 技巧 param image 知识
最大流问题就是求出一个可行流量使得从单源点到单汇点的流网络中流量最大。
且满足如下约束:
增广路 \(Augmenting \ Path\)
一条能够增加源点到汇点的流量的路径
残余网络 \(Residual \ Network\)
仍然有容量的边构成的网络就是残余网络。最初始时,\(残余网络=原网络\)
残余网络中我们增加了一种特殊的反向边。反向边的作用在于当我们找的增广路不是最优的时候,能够进行"反悔",因为正向反向都传递相同流量\(\Leftrightarrow\)取消该流量
关键边 一条增广路上流量\(=\)容量的边,即恰好能被流量填满的边。
最大流\(=\)最小割\(\ maximum \ flow = minimum \ cut\)
最小割问题:把边权看作割去某条边代价,找到一种割边的方案使得源点\(s\)和汇点\(t\)不连通所付出的代价最小。
如下图
可行的最大流为23
增广路有:
\(A \rightarrow B \rightarrow C \rightarrow E\)
\(A \rightarrow C \rightarrow E \rightarrow F\)
\(A \rightarrow C \rightarrow B \rightarrow D \rightarrow F\)
\(A \rightarrow C \rightarrow E \rightarrow D \rightarrow F\)
若用\(最大流 = 最小割\)的角度来看,最小割为\((E,D) + (E,F) + (B,D)\),加起来恰好就是23.
初始化流量\(maxFlow\)为0,初始化残余网络\(rG\)为原网络。
每一趟增广:
关键边的总数为\(O(VE)\),每一趟赠广必然会将一条关键边填满,所以最多\(O(VE)\)趟,总的时间复杂度为\(O(VE^2)\)
具体证明请参阅《算法导论》,鄙人能力有限~
#define V 1e3;
#define E 1e4;
vector<vector<int> > res; //残余网络
vector<vector<int> > g; //邻接表存储【双向图】,因为需要利用到反向边
/*
* 查找是否还存在s->t的增广路
* @param s 源点
* @param t 汇点
* @param fa[] 存储前驱顶点,整体为一条增广路
* @return 若有增广路,返回增广的流量,否则返回0
*/
int bfs(int res[][V], int s, int t, int fa[]) {
fill(fa.begin(), fa.end(), -1);
fa[s] = -2;
// pair的第二参数用来在查找增广路的同时顺便找到最大流量
queue<pair<int,int> > q;
q.push({s, INF});
while (q.size()) {
int u = q.front().first;
int f = q.front().second; q.pop();
for (const v& next: adj[u]) {
if (fa[v]==-1 && res[u][v]) { // 能够增广
fa[v] = u;
int newF = min(f, res[u][v]);
if (v == t) return newF;
else q.push({v, newF});
}
}
}return 0;
}
int fordFulerson(int s, int t) {
int maxFlow = 0;
vector<int> fa(n); // 前驱数组,用于记录路径
while (int pathF = bfs(res, s, t, fa)) {
maxFlow += pathF;
int u = t;
while (u != s) {// 增加流量之后,沿着找到的增广路更新残余网络
res[ fa[u] ][u] -= pathF;
res[u][ fa[u] ] += pathF; // 反向边增加容量
u = fa[u];
}
}return maxFlow;
}
类似于Edmond-Karp算法,Dinic算法同样使用了以下的观点:
相较于Edmond-Karp算法,Dinic算法的优化之处在于BFS查找增广路时得到的不是仅仅一条增广路,而是一张增广网络,同时进行多路增广,有些像Edmond-Karp算法的并发版本。这张增广网络的基础就是一张层级网络,BFS的过程就是给每一个点赋予一个层级建立层级网络,之后沿着多条层级逐一递增(0,1,2,3...)的路径传送流量。当层级网络无法构建时,说明当前流量已经最大。
传送流量的过程中,在每一次传送完之后都会出现已经充满流量的边,这些边已经不能够再为增加流量做贡献,为了避免DFS传送流量时重复遍历这些无用边,利用一个指针来指向当前能够利用的第一条边。
每一趟增广:
最多\(O(V)\)躺结束,总的时间复杂度\(O(V^2E)\).
/*
普通图的情况下:复杂度O(V2 E)
二分图下的复杂度:O(根号(VE))
*/
const int maxv = 10004, maxe = 200004;
// 链式向前星存图,双向图
struct Edge {
int next, to, cap; /*cap 为边的残余容量 */
Edge(int a=0, int b=0, int c=0):to(a), cap(b), next(c) {}
}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] = Edge(to, cap, head[from]);
head[from] = cnte;
//反向边, 初始的容量为0
cnte += 1;
e[cnte] = Edge(to, cap, head[from]);
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;
}
// 增广流量
// 自底向上传递
// @param in 为源点输入到这个点上的最大流量
// @param now 当前顶点
// @return 到达now时传进的最大流量
int dfs(int now, int t, int in) {
if (!in || now==t)
return in;
//i为cur[now]的引用-->cur[now]随着i改变
// 保证了cur[now]为第一个有用的边
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经边e[i]流向nv的流量
int out = dfs(nv, t, min(in, e[i].cap)); //一路上都受到最小流量的限制
// 能够传送说明这条路径上必然填满了一条关键边
// 那么这次的流量传送就可以结束了
if (out > 0) {
e[i].cap -= out;
e[i^1].cap += out; //反向边,用了下异或1的小技巧
return out;
}
}
}
return 0; //没有增广路,返回0
}
// 当前弧优化Dinic
// 注意是两个while,每次bfs找到多个增广路后
// 多次dfs计算网络中所有增广路的流量
int dinic(int s, int t) {
int maxFlow = 0;
while (bfs(s, t))
{
//每次建立完层次图之后记录当前弧
for (int i=1; i<=n; ++i)
cur[i] = head[i];
int flow=0;
while (flow = dfs(s, t, INF))
maxFlow += flow;
}
return maxFlow;
}
References
1.ford-fulkerson-algorithm-for-maximum-flow-problem
2.dinics-algorithm-maximum-flow
3.graph-dinic
4.stanford-network-flow-problems
5.cornell-edmondskarp
标签:最小流 const 顶点 new 邻接 技巧 param image 知识
原文地址:https://www.cnblogs.com/GorgeousBankarian/p/12220835.html