[网络流]最大流算法 Dinic
来源:互联网 发布:hbase数据库设计原则 编辑:程序博客网 时间:2024/05/16 15:33
类似之前的储存方法,但稍作修改,代码如下:
struct Edge { int from, to, cap, flow;};这样储存的是一条边。在做题的过程中,发现一个技巧(也算是技巧吧),出现无向边时,不需要加两条边,而是直接将反向边的容量也改为cap,会减少很多多余的时间和空间。
插入边的过程与之前相同:
void AddEdge(int from, int to, int cap) { edges.push_back((Edge) { from, to, cap, 0 }); edges.push_back((Edge) { to, from, 0, 0 }); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1);}
Dinic算法中,主要的操作过程如下:通过BFS构造层次图,然后DFS一次增广。
首先先来看看层次图。
在残量网络中,s到节点v的距离为d,那么d就是节点v的层次。只保留每个点到下一个层次的弧,也就是d(u) + 1 = d(v),的图,就是层次图。
层次图上的任意一条路径都是从s到层次1到层次2……直到t,可以发现,每一条这样的路径都是一条s-t最短路,这样求,不会出现走多余的边的情况。
如图,这样的一个图就是一个层次图。
从1开始,2和3都是层次1,4和5是层次2,6是层次3
在层次图上DFS求最大流自然很简单,就是不考虑反向边时能得到的最大流,多次增广后,重新计算层次图,发现s和t不连通,就退出。
它的时间复杂度不会很高,相对Ek会快很多,最多只会跑n-1次DFS,因为每次DFS之后s到t的路径至少会少一条,也就是最大距离至少会增加1,而每一次跑DFS最多会用nm的时间,每一次最多遍历每一个节点,每一个节点遍历它所链接的所有边,所以最终时间复杂度为O(N^2*M),相对Ek来说会快很多。
实际上,理论数值所对应的数据几乎不会出现,通常并不会想理论值说的这么慢,只会更快。
而且,对于它,还有一个很重要的优化,对于DFS来说,要保存一个“当前所有弧的最小残量”,如果等于0,必然是不能增广的,找到一条路径直接返回它的值即可,不然会出现多路增广不会退出的情况,还需要多加一次再算增广量,必然会很慢。
还有,当前弧优化,对于一次DFS中,可能会重复访问一个节点,虽然可能性比较小,这时候,已经走过的边自然是不需要再走,那么用一个数组记录上一次这个点走了哪些连接它的边,再次访问时不再重复走,那么就会更快。
下面为代码:
struct Dinic { int n, m, s, t; vector < Edge > edges; vector < int > G[MAXN]; bool vis[MAXN]; int d[MAXN]; int cur[MAXN]; void AddEdge(int from, int to, int cap) { edges.push_back((Edge) { from, to, cap, 0 }); edges.push_back((Edge) { to, from, 0, 0 }); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool BFS() { memset(vis, 0, sizeof vis); queue < int > Q; Q.push(s); d[s] = 0; vis[s] = 1; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); ++i) { Edge& e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { //如果未曾访问过,而且这一条边处于残量网络中,那么计算层次 vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) { if(x == t || a == 0) { // 如果找到一条增广的路或无法继续增广,返回当前流量 return a; } int flow = 0, f; for(int& i = cur[x]; i < G[x].size(); ++i) { Edge& e = edges[G[x][i]]; if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) { // 如果处于同一个层次且这一条路径还能增广 e.flow += f; edges[G[x][i] ^ 1].flow -= f; flow += f; a -= f;//增广 注意当前流量a要减f if(a == 0) {//如果a=0,那么不能继续增广,返回上一条路 break; } } } return flow; } int Maxflow(int s, int t) { this -> s = s; this -> t = t; int flow = 0; while(BFS()) { // 计算层次图 如果s-t不连通那么退出 memset(cur, 0, sizeof cur); // 初始化当前弧 flow += DFS(s, INF); // 计算最大流 } return flow; }} dinic;
当然,这里的DFS用的是递归实现的,但是如果Dinic依然会超时,那么可以尝试将DFS写成迭代的过程,会提高一些速度,但是也会相应地提高代码量,不如直接使用ISAP,不仅更快,而且代码稍微少一些。
- 最大网络流Dinic算法
- 网络最大流 dinic算法
- [网络流]最大流算法 Dinic
- Dinic算法(网络流,最大流)
- Dinic算法 P3376 【模板】网络最大流
- 最大网络流模板Dinic算法
- dinic网络最大流
- Dinic算法最大流。。
- 最大流dinic算法
- 最大流 dinic算法
- 最大流 dinic算法
- 最大流-dinic算法
- 最大流-Dinic算法
- 最大流 Dinic算法
- 网络流模板:最大流ISAP算法和Dinic算法
- poj1273(网络流最大流 EK算法&&dinic算法)
- 网络流模板:最大流ISAP算法和Dinic算法
- 最大流算法 -- Dinic算法
- Spring4 整合 Hibernate4
- windows连虚拟机mongodb
- 如何产生已知概率密度函数的随机数?
- hdu 1242 Rescue
- LintCode之报数
- [网络流]最大流算法 Dinic
- django 注册、登录、第三方接口程序
- 对JAVASCRIPT匿名函数的理解(透彻版)
- 5-37 模拟EXCEL排序
- c++跨文件变量声明
- NYOJ 353 Dungeon Master【BFS】
- 第二章.一切都是对象之基本类型【主数据类型】数组
- Android 使用 JSON 保存应用数据
- thinkphp中无法解析__PUBLIC__ 模板常量