网络流学习笔记

  最大流: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     }
View Code

  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     }
View Code

   网络流主要应用于建模,通常是流量负荷模型,其中由流量(需求)

限制(负荷)贡献三部分构成,根据限制条件合理构造满足条件的模型,

注意有向这一建图基础,它意味着你需要分析问题性质,所构造的模型

必须具有方向性。

  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 }
View Code

   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 }
View Code

   最小割:网络流图的一种划分方式,应用于给定限制下图的划分。

  关于最大流最小割定理,最大流状态下显然限制条件为流量限制的极

小边集,此时由最小割定义知,由极小边集中选择能割断网络流的边集一

定最优。

  利用定义,由于最小割由极小子集中若干组成,而最大流中极小子集权

值必定为0,故由源点出发dfs遍历权值大于0的边即可。

  利用最大流等于最小割定理,将边权设置为1,遍历权值为0的边统计答

案即可计算最小割边数。

posted @ 2021-07-31 16:56  HZOI_LYM  阅读(77)  评论(0编辑  收藏  举报