网络流最大流之Dinic算法
Posted jionkitten
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流最大流之Dinic算法相关的知识,希望对你有一定的参考价值。
主要参考了这篇博客来学习,个人觉得还是讲的比较通俗易懂的:https://blog.csdn.net/weixin_43907802/article/details/84705855
还是debug了很久。。一开始是抄错模板,后来是忘开了二倍数组和错用了对称性,然后是疯狂爆int。。总而言之,bug真的是一辈子都de不完的。。
贴个代码,注释也直接从模板搬过来了:
#include <iostream> #include <stdio.h> #include <cstring> #include <queue> #define emax 5005 #define pmax 105 #define INF 10000000000 // (1 << 31) 会爆int(可能是位运算默认只有32位?) // #define LOCAL using namespace std; struct Edge { int next, to; long long left; } edge[emax << 1]; // 记得开双倍。。 int head[pmax], e = -1; struct Dinic { //cur用于当前弧优化, dep指最小的深度 int dep[pmax], cur[pmax], s, t, n; queue<int> q; bool bfs() { int u, v; memset(dep, 0xff, sizeof(dep)); dep[s] = 0; q.push(s); while (!q.empty()) { u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].next) { v = edge[i].to; if (dep[v] == -1 && edge[i].left > 0) //注意要找增广路,残余容量大于0 (前面是等于-1,一开始抄错了) { dep[v] = dep[u] + 1; q.push(v); } } } return dep[t] != -1; //d[v]!=-1表示存在s到t的增广路 } //函数的返回类型也要改为long long,这点很容易忽略,一般都是去找变量声明,而忽略了函数声明(其实是自己太菜) long long dfs(int u, long long flow) //flow为增广路s->u上最小残余容量,也可以看作是当前可以(或是说能够)流经u的流量吧 { #ifdef LOCAL // for (int i = 1; i <= n; ++i) cout << dep[i] << endl; #endif int v; long long tmp; if (u == t || !flow) return flow; //已到汇点,增流flow或者残余容量为0无法增广就返回 long long f = 0; //f表示经过u的流量有多少 for (int &i = cur[u]; ~i; i = edge[i].next) { /*cur当前弧优化,记录访问到的边,由于是从前往后增广, 访问到某边时,说明前面的边已无法再增广,利用cur跳过前面废边提高效率(注意引用,这点很妙) */ v = edge[i].to; if (dep[v] == dep[u] + 1 && (tmp = dfs(v, min(flow, edge[i].left)))) { #ifdef LOCAL cout << u << " -> " << v << " : " << tmp << endl; #endif edge[i].left -= tmp; edge[i ^ 1].left += tmp; f += tmp, flow -= tmp; //多路增广,尽可能把前面flow的流量都流完再返回 if (!flow) break; } } return f; } //注意函数声明返回类型 long long maxflow(int s, int t, int n) { this->s = s, this->t = t, this->n = n; long long ans = 0; while (bfs()) { for (int i = 1; i <= n; ++i) cur[i] = head[i]; ans += dfs(s, INF); //一开始能流经s的流量是不确定的,因为并没有连向s的路,s为起点,故设为无穷大 } return ans; } }; int main() { #ifdef LOCAL freopen("out.txt", "w", stdout); #endif int n, m, s, t, u, v; long long c; scanf("%d%d%d%d", &n, &m, &s, &t); memset(head, 0xff, sizeof(head)); for (int i = 0; i < m; ++i) { scanf("%d%d%lld", &u, &v, &c); //lld读入 edge[++e].next = head[u], edge[e].to = v, edge[e].left = c, head[u] = e; edge[++e].next = head[v], edge[e].to = u, edge[e].left = 0, head[v] = e; /* 这里注意不是对称的,反向边left 应设为0,以后也要注意类似的对称模式 */ } Dinic solve; cout << solve.maxflow(s, t, n); }
还有一点没想通就是如果数据中同时存在两个点的正反向边,那么后加入的边的反向边可能会覆盖在前一条边上,会不会出现什么问题。。不过数据还是过了,很玄学
以上是关于网络流最大流之Dinic算法的主要内容,如果未能解决你的问题,请参考以下文章