网络流学习笔记
最大流:dfs下由于目标不确定性使得使非最大流集合的路径
消耗流改变原图结构,从而无法遍历出正解
于是考虑反悔,建立反向边残量网络,考虑意义,若存在一条
路径经过反向边,代表存在另一种增广路的选择,观察反向边路径
发现,其本质为将之前已经运输的流量运输到另一位置,也就是反
悔,而当图中不存在残量网络时,代表当前状态下已经不存在一种
可能的增广路,由于最大流必定为一种固定状态,此时当不存在可
能路径时,即为最大流状态(dfs性质决定),事实上其本质为遍历
每一种可能情况直至找到目标状态。
思想:逆向存储建立反悔系统,固定状态存在的必然性,构造法
EK优化:由于邻接表遍历的限制,单纯带悔dfs存在的致命缺陷
为目标的不确定性(但一定会找到目标),这导致算法效率的不确定
性,于是EK算法对于FF算法的优化就在于赋予程序以一定的顺序,
将dfs改为bfs,每次遍历最短增广路,可知增广次数在于每条边满流
的次数之和,而一条边满流代表增广路长度的增加(逻辑证明),增
广路最多增加V次于是算法复杂度O(VE^2)
思想:不确定性算法的优化->赋予算法以一定顺序,复杂度证明
代码如下:
1 namespace Edmonds_Krap { 2 LL maxflow; 3 I flow[N],pre[N]; 4 B jud[N]; 5 B bfs () { 6 memset (jud,0,sizeof jud); 7 queue <I> q; 8 q.push (s); jud[s] = 1; 9 flow[s] = INT_MAX; 10 while (!q.empty ()) { 11 I x (q.front ()); q.pop (); 12 for (I i(head[x]); i ;i = nxt[i]) 13 if (wgt[i] && !jud[to[i]]) { 14 flow[to[i]] = min (flow[x],wgt[i]); 15 pre[to[i]] = i; 16 if (!(to[i] ^ t)) return true; 17 q.push (to[i]); jud[to[i]] = 1; 18 } 19 } 20 return false; 21 } 22 V update () { 23 I x (t); 24 while (x != s) { 25 wgt[pre[x]] -= flow[t]; wgt[pre[x] ^ 1] += flow[t]; 26 x = to[pre[x] ^ 1]; 27 } 28 maxflow += flow[t]; 29 } 30 V Edmonds_krap () { while (bfs ()) update (); } 31 }
DC优化:容易发现EK优化算法复杂度瓶颈在于每次只拓展一条
增广路,这样许多上流量将被浪费,于是考虑充分利用上流量进行多
路拓展,为了保证复杂度,仍然需要保证拓展的有序性,于是选择在
分层图上进行拓展,当前节点流量对子节点进行多次拓展,容易发现
此情况下dfs无法标记成为指数级算法,考虑优化无用遍历,发现当一
条路径流量被充分利用时,下一次拓展不会造成新的贡献,于是考虑
记录已经不会造成贡献的节点避免下次遍历。需要注意的是,当前弧
优化须注意避免最后一个节点被略过,考虑for运行顺序合理设计即可
思想:充分利用资源进行拓展,避免冗余遍历,时间复杂度优化
代码如下:
1 namespace Dinic { 2 LL maxflow; 3 I d[N],now[N]; 4 B bfs () { 5 memset (d,0,sizeof d); 6 memcpy (now,head,sizeof head); 7 queue <I> q; 8 q.push (s); d[s] = 1; 9 while (!q.empty ()) { 10 I x (q.front ()); q.pop (); 11 for (I i(head[x]); i ;i = nxt[i]) 12 if (wgt[i] && !d[to[i]]) { 13 d[to[i]] = d[x] + 1; 14 if (! (to[i] ^ t)) return true; 15 q.push (to[i]); 16 } 17 } 18 return false; 19 } 20 I dfs (I x,I flow) { 21 if (! (x ^ t)) return flow; 22 I rest (flow),sup; 23 for (I i = now[x]; i ;i = nxt[i]) 24 if (wgt[i] && d[to[i]] == d[x] + 1) { 25 sup = dfs (to[i],min (rest,wgt[i])); 26 if (!sup) d[to[i]] = 0; 27 wgt[i] -= sup, wgt[i ^ 1] += sup; rest -= sup; 28 if (!rest) { now[x] = i; break; } 29 } 30 return flow - rest; 31 } 32 V dinic () { while (bfs ()) maxflow += dfs (s,INT_MAX); } 33 }
网络流主要应用于建模,通常是流量负荷模型,其中由流量(需求)
限制(负荷)贡献三部分构成,根据限制条件合理构造满足条件的模型,
注意有向这一建图基础,它意味着你需要分析问题性质,所构造的模型
必须具有方向性。
update :事实上网络流与线性空间存在很多相似之处,可以将网络流
抽象的视作图论下的高斯消元,即在若干点边的线性关系间求解需求解。
个人问题:考虑供给需求模型与流量负荷模型的差异性,最大流问题
中利用最大流存在的必然性,以被动负荷(满载)寻求最大流,这里很显
然存在被动性,而供给需求模型则不符合这一性质,被动性与主动性的差
异导致模型设计过程中限制条件的设置。
T1蜥蜴中,构造模型,流量为蜥蜴数量(理想最大流),限制条件为
石柱使用次数,此处为满足模型被动性,在设计过程中,利用拆点构造一
条虚边来满足次数限制,而成功运载量即为流量最终贡献。
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define C char 5 #define B bool 6 #define V void 7 #define D double 8 #define LL long long 9 const I N1 = 25; 10 const I N2 = 405; 11 I r,c,d,s,t,tot,num,p; 12 I head[N2*4],to[N2*N2],nxt[N2*N2],wgt[N2*N2]; 13 I sto[N1][N1][2]; 14 C init[N1]; 15 inline V found (I x,I y,I z) { 16 to[++tot] = y,wgt[tot] = z,nxt[tot] = head[x],head[x] = tot; 17 to[++tot] = x,wgt[tot] = 0,nxt[tot] = head[y],head[y] = tot; 18 } 19 inline B jud (I x1,I y1,I x2,I y2) { 20 D tmp = sqrt ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); 21 return tmp <= (D) d; 22 } 23 namespace WYZG { 24 namespace Edmonds_Krap { 25 LL maxflow; 26 I flow[N2*4],pre[N2*4]; 27 B jud[N2*4]; 28 B bfs () { 29 memset (jud,0,sizeof jud); 30 queue <I> q; 31 q.push (s); jud[s] = 1; flow[s] = INT_MAX; 32 while (!q.empty ()) { 33 I x (q.front ()); q.pop (); 34 for (I i(head[x]); i ; i = nxt[i]) 35 if (wgt[i] && !jud[to[i]]) { 36 flow[to[i]] = min (flow[x],wgt[i]); 37 pre[to[i]] = i; 38 if (to[i] == t) return true; 39 q.push (to[i]); jud[to[i]] = 1; 40 } 41 } 42 return false; 43 } 44 V update () { 45 I x (t); 46 while (x != s) { 47 wgt[pre[x]] -= flow[t], wgt[pre[x] ^ 1] += flow[t]; 48 x = to[pre[x] ^ 1]; 49 } 50 maxflow += flow[t]; 51 } 52 V Edmonds_Krap () { while (bfs ()) update (); } 53 } 54 namespace Dinic { 55 LL maxflow; 56 I d[N2*4],now[N2*4]; 57 B bfs () { 58 memset (d,0,sizeof d); 59 memcpy (now,head,sizeof head); 60 queue <I> q; 61 q.push (s); d[s] = 1; 62 while (!q.empty ()) { 63 I x (q.front ()); q.pop (); 64 for (I i(head[x]); i ; i = nxt[i]) 65 if (wgt[i] && !d[to[i]]) { 66 d[to[i]] = d[x] + 1; 67 if (to[i] == t) return true; 68 q.push (to[i]); 69 } 70 } 71 return false; 72 } 73 I dfs (I x,I flow) { 74 if (x == t) return flow; 75 I rest (flow),sup; 76 for (I i(now[x]); i ;i = nxt[i]) 77 if (wgt[i] && d[to[i]] == d[x] + 1) { 78 sup = dfs (to[i],min (wgt[i],rest)); 79 if (!sup) d[to[i]] = 0; 80 wgt[i] -= sup, wgt[i ^ 1] += sup, rest -= sup; 81 if (!rest) { now[x] = i; break; } 82 } 83 return flow - rest; 84 } 85 V dinic () { while (bfs ()) maxflow += dfs (s,INT_MAX); } 86 } 87 } 88 using namespace WYZG; 89 signed main () { 90 cin >> r >> c >> d; 91 tot = 1; s = ++num; 92 for (I i(1);i <= r; ++ i) { 93 cin >> init + 1; 94 for (I j(1);j <= c; ++ j) if (init[j] ^ 48) { 95 sto[i][j][1] = ++num, sto[i][j][0] = ++num; 96 found (sto[i][j][1],sto[i][j][0],init[j] ^ 48); 97 } 98 } 99 for (I i(1);i <= r; ++ i) { 100 cin >> init + 1; 101 for (I j(1);j <= c; ++ j) if (init[j] != '.') 102 found (s,sto[i][j][1],1), ++ p; 103 } 104 t = ++num; 105 for (I i(1);i <= r; ++ i) 106 for (I j(1);j <= c; ++ j) if (sto[i][j][0]) 107 for (I k(1);k <= r; ++ k) 108 for (I l(1);l <= c; ++ l) if (sto[k][l][1]) 109 if (jud (i,j,k,l)) found (sto[i][j][0],sto[k][l][1],INT_MAX); 110 for (I i(1);i <= r; ++ i) 111 for (I j(1);j <= c; ++ j) if (sto[i][j][0]) 112 if (j < d + 1 || j > c - d || i < d + 1 || i > r - d) 113 found (sto[i][j][0],t,INT_MAX); 114 Dinic :: dinic (); 115 //Edmonds_Krap :: Edmonds_Krap (); 116 cout << p - Dinic :: maxflow << endl; 117 //cout << p - Edmonds_Krap :: maxflow << endl; 118 }
T2:利用最大流二分答案转判定,流量负荷模型,注意限制条件的判
断与设定。
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define C char 5 #define B bool 6 #define V void 7 #define D double 8 #define LL long long 9 const I N1 = 55; 10 const I N2 = 5005; 11 const D eps = 1e-5; 12 I n,m,tot,num,s,t,sigma; 13 I rob[N1],egy[N1],head[N1*2],nxt[N2*2],to[N2*2],ord1[N1],ord2[N1]; 14 D wgt[N2*2]; 15 B init; 16 inline V found (I x,I y,D z) { 17 to[++tot] = y,wgt[tot] = z,nxt[tot] = head[x],head[x] = tot; 18 to[++tot] = x,wgt[tot] = 0,nxt[tot] = head[y],head[y] = tot; 19 } 20 namespace WYZG { 21 namespace Dinic { 22 D maxflow; 23 I d[N1*2],now[N1*2]; 24 D val[N2*2]; 25 B bfs () { 26 memset (d,0,sizeof d); 27 memcpy (now,head,sizeof head); 28 queue <I> q; 29 q.push (s); d[s] = 1; 30 while (!q.empty ()) { 31 I x (q.front ()); q.pop (); 32 for (I i(head[x]); i ; i = nxt[i]) 33 if (val[i] > eps && !d[to[i]]) { 34 d[to[i]] = d[x] + 1; 35 if (to[i] == t) return true; 36 q.push (to[i]); 37 } 38 } 39 return false; 40 } 41 D dfs (I x,D flow) { 42 if (x == t) return flow; 43 D rest (flow),sup; 44 for (I i(now[x]); i ;i = nxt[i]) 45 if (val[i] > eps && d[to[i]] == d[x] + 1) { 46 sup = dfs (to[i],min (rest,val[i])); 47 if (sup < -eps) d[to[i]] = 0; 48 val[i] -= sup; val[i xor 1] += sup; rest -= sup; 49 if (rest < -eps) { now[x] = i; break; } 50 } 51 return flow - rest; 52 } 53 B dinic () { while (bfs ()) maxflow += dfs (s,INT_MAX); return fabs (maxflow - (D)sigma) < eps; } 54 } 55 namespace DIC { 56 D dic (D l,D r) { 57 while (l + eps < r) { 58 D mid = (l + r) / 2; 59 Dinic :: maxflow = 0; 60 memcpy (Dinic :: val,wgt,sizeof wgt); 61 for (I i(head[t]); i ;i = nxt[i]) Dinic :: val[i xor 1] *= mid; 62 Dinic :: dinic () ? r = mid : l = mid; 63 } 64 return l; 65 } 66 } 67 } 68 using namespace WYZG; 69 signed main () { 70 cin >> n >> m; 71 tot = 1; s = ++num; 72 for (I i(1);i <= n; ++ i) cin >> rob[i], ord1[i] = ++num; 73 for (I i(1);i <= m; ++ i) cin >> egy[i], ord2[i] = ++num; 74 for (I i(1);i <= n; ++ i) found (s,ord1[i],(D)rob[i]), sigma += rob[i]; 75 for (I i(1);i <= m; ++ i) 76 for (I j(1);j <= n; ++ j) if (cin >> init, init) 77 found (ord1[j],ord2[i],(D)INT_MAX); 78 for (I i(1);i <= m; ++ i) found (ord2[i],t,(D)egy[i]); 79 printf ("%.5lf",DIC :: dic (0,1e5)); 80 }
最小割:网络流图的一种划分方式,应用于给定限制下图的划分。
关于最大流最小割定理,最大流状态下显然限制条件为流量限制的极
小边集,此时由最小割定义知,由极小边集中选择能割断网络流的边集一
定最优。
利用定义,由于最小割由极小子集中若干组成,而最大流中极小子集权
值必定为0,故由源点出发dfs遍历权值大于0的边即可。
利用最大流等于最小割定理,将边权设置为1,遍历权值为0的边统计答
案即可计算最小割边数。