初识网络流

    研究网络流快半个月了,只觉得自己学的太慢。学长说模板什么的,码多了就明白什么意思了,可咱总是想先了解清楚不是。。。

    昨天校赛上,一道费用流把咱坑住了,这不是还没来得及看么,被队友好一顿数落 T~T

    今天又花了三个小时才把sap的模板看明白了。。。人家学网络流都是拿着模板上来就做题,差距啊   O(∩_∩)O哈哈~

下面晒一晒学习成果

EK:十分暴力,不过刚上手还是从简单的来吧

Edmond-Karp
 1 #define N 1000
 2 #define Inf 9999999
 3 
 4 int G[N][N],c[N][N],f[N][N],pre[N],que[N],n,vis[N];
 5 
 6 int ek(int s,int t)
 7 {
 8     int i,j,k,head,tail,flow=0;
 9     memset(f,0,sizeof(f));
10     while(true)
11     {
12         head=tail=0;
13         memset(vis,0,sizeof(vis));
14         que[tail++]=s;
15         vis[s]=true;
16         while(head<tail)                 //寻找 增广路
17         {
18             k=que[head++];
19             if(k==t)
20                 break;
21             for(i=1;i<=n;i++)
22             {
23                 if(c[k][i]>0&&!vis[i])  // 入队:沿有向边方向dfs汇点t
24                 {
25                     vis[i]=true;
26                     pre[i]=k;
27                     que[tail++]=i;
28                 }
29             }
30         }
31         if(k!=t)                        //如果找不到增广路:结束
32             break;
33         int cc=Inf;                     // cc:增广路上边权的最小值
34         j=t;
35         i=pre[j];
36         while(j!=s)                     //初走增广路:获取最小值
37         {
38             if(c[i][j]<cc)
39                 cc=c[i][j];
40             j=i;
41             i=pre[j];
42         }
43         flow+=cc;                       //flow:当前最大流 
44         j=t;
45         i=pre[j];
46         while(j!=s)                     //重走增广路:更新残流、反向边
47         {
48             f[i][j]+=cc;
49             f[j][i]=-f[i][j];
50             c[i][j]=G[i][j]-f[i][j];
51             c[j][i]=G[j][i]-f[j][i];
52             j=i;
53             i=pre[j];
54         }
55     }
56     return flow;
57 }

dinic:引入了层次的概念,不用担心“绕圈的”被无视了

dinic
 1 #define Min(a,b) a<b?a:b  
 2 #define N 10000  
 3 #define Inf 9999999  
 4 struct Edge  
 5 {  
 6     int to,cap,opt,next;  
 7 }e[100000];                                        
 8 int ec,pp[N],n,s,t;                                //pp[]:邻接表头表
 9 int level[N],que[N];  
10   
11 bool makelevel()                                   //标记层次
12 {  
13     int i,j,k,head=0,tail=0;  
14     memset(level,-1,sizeof(level));  
15     level[s]=0;  
16     que[tail++]=s;  
17     while(head<tail)  
18     {  
19         k=que[head++];  
20         for(i=pp[k];i!=-1;i=e[i].next)  
21             if(e[i].cap>0&&level[e[i].to]==-1)    //遍历队从队首节点出发的边&未标记等级的:等级+1,并入队  
22             {  
23                 level[e[i].to]=level[k]+1;  
24                 que[tail++]=e[i].to;  
25             }  
26     }  
27     return level[t]!=-1;                         //若汇点t未被标记
28 }  
29   
30 int findpath(int u,int alpha)                    // DFS:从源点到汇点,参数:当前点,这条路上的最小值
31 {  
32     if(u==t)  
33         return alpha;  
34     int i,j,k,w=0;  
35     for(i=pp[u];i!=-1;i=e[i].next)  
36         if(e[i].cap>0&&level[e[i].to]==level[u]+1)          
37             if(k=findpath(e[i].to,Min(e[i].cap,alpha-w)))   
38             {  
39                 e[i].cap-=k;  
40                 e[e[i].opt].cap+=k;  
41                 w+=k;
42             }  
43     return w;  
44 }  
45   
46 int dinic()  
47 {  
48     int i,j,k=0;  
49     while(makelevel())  
50         while(i=findpath(s,Inf))         //在增广路存在时,i=增广路上的最小值;否则,无增广路,输出k
51                                            while(makelevel())不会无限标记
52             k+=i;  
53     return k;  
54 }  

dinic的常数优化:走岔路。BFS+DFS,思路很清晰

源自http://happylch21.blog.163.com/blog/static/16563975920116259323343/

View Code
 1 #include<stdio.h>
 2 #include<string.h>
 3 
 4 #define MIN(a,b) a > b ? b : a
 5 #define NUM 250
 6 #define INF 0x7ffffff
 7 
 8 typedef struct {
 9     int e,f,next;
10 }info;
11 info edge[NUM * NUM];
12 
13 int head[NUM];
14 int Que[NUM];
15 int level[NUM];
16 int st,ed;
17 int tol;
18 
19 void Add(int s,int t,int c)                                                     //构建邻接表
20 {
21     edge[tol].e = t;
22     edge[tol].f = c;
23     edge[tol].next = head[s];
24     head[s] = tol ++;
25 
26     edge[tol].e = s;                                                            //构建反向边
27     edge[tol].f = 0;
28     edge[tol].next = head[t];
29     head[t] = tol ++;
30 }
31 
32 int BFS()
33 {
34     int fr,tp,next,cur_level,cur;
35     memset(level,-1,sizeof(level));
36     Que[0] = st;
37     level[st] = 0;
38     for( fr = 0,tp = 1; fr != tp; fr = (fr + 1)%NUM ){
39         cur = Que[fr];
40         cur_level = level[cur];
41         for(next = head[cur]; next != -1; next = edge[next].next){
42             if( edge[next].f && level[ edge[next].e ] == -1 ){
43                 Que[tp] = edge[next].e;
44                 level[ edge[next].e ] = cur_level + 1;
45                 tp = (tp + 1)%NUM;
46             }
47         }
48     }
49     return level[ed] != -1;
50 }
51 
52 int DFS(int s,int min)
53 {
54     int r = 0,next;                                                             // r 是 "亮点"
55     int t;
56     if( s == ed ) return min;
57     for( next = head[s]; r < min && next != -1;next = edge[next].next ){
58         if( edge[next].f && level[s]+1== level[ edge[next].e ] ){
59             t = MIN( min - r,edge[next].f );                                    // 取当前路得最小值
60                                                                                 // 第一次经过 (无岔路) r = 0 <=> ( min - r )到目前为止各边的最小值 ; edge[next].f 当前边权
61                                                                                 // 再次经过 (有岔路)   r!=0 <=> r 是已增广的最大流
62                                                                                 //                  (min-r)<=> 到当前路为止仍能通过的最大残流
63             t = DFS(edge[next].e,t);
64             r += t;
65             edge[next].f -= t;
66             edge[next^1].f += t;
67         }
68     }
69     if( !r )level[s] = -2;
70     return r;
71 }

sap:反正看上去好复杂,标记层次+寻找增广路+优化用的cur[],全都到一块了,膜拜设计模板的大牛

sap
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <iostream>
 6 using namespace std;
 7 const int maxn = 210;
 8 const int maxm = 1123123;
 9 const int inf = 1000000009;
10 struct E {
11     int v, next, c;
12 }e[maxn*maxn<<2];
13 int tot, head[maxn];
14 int n, m;
15 
16 void init() {
17     tot = 0;
18     memset(head, -1, sizeof(head));
19 }
20 
21 void add(int s, int t, int c) {                                                 //构建邻接表
22     e[tot].v = t;
23     e[tot].c = c;
24     e[tot].next = head[s];
25     head[s] = tot++;
26     e[tot].v = s;
27     e[tot].c = 0;
28     e[tot].next = head[t];
29     head[t] = tot++;
30 }
31 
32 int gap[maxn];                                                                  // gap[]: 记录各层次点的数量
33 int dis[maxn];                                                                  // dis[]: 记录各点的层次
34 int pre[maxn];                                                                  // pre[]: 记录当前点的前驱
35 int cur[maxn];                                                                  // cur[]: 邻接表头表 head[]的替身,通过不断更新,并配合条件 e[i].c>0,寻找最近的允许边
36 
37 int sap(int s, int t, int n)                                                    // s 源点,t汇点,n顶点总数
38 {
39     int i;
40     for(i = 0; i <= n; i++) {
41         dis[i] = gap[i] = 0;
42         cur[i] = head[i];
43     }
44     gap[0] = n;                                                                 //初始化 所有点的层次都是 0
45     int u = pre[s] = s, maxf = 0, aug = inf, v;
46     while(dis[s] < n)
47     {
48 loop:   for(i = cur[u]; i != -1; i = e[i].next)                                 // "部分" 遍历邻接表 注意: cur[u]=i(见54行) 头表不断在更新
49         {
50             v = e[i].v;
51             if(e[i].c > 0 && dis[u] == dis[v] + 1) {
52                 aug = min(aug, e[i].c);                                         //为了能使 u==t (见56行) 这一条件满足时直接进行增广,aug取所有允许边中的最小值。同时,体现了 cur[u]不断维护的优点,可以立刻再次沿增广路增广,若已完成增广,会跳过 不允许边 ,继续操作
53                 pre[v] = u;
54                 cur[u] = i;                                                     //在邻接表中顺序靠前的:包括当前节点若存在层次较高(与父节点层次相同)的兄弟节点,或边权为零(未增广的反向边,已增广的正向边),做此操作,等价于忽略其之前的兄弟节点
55                 u = v;
56                 if(u == t) {
57                     while(u != s) {
58                         u = pre[u];
59                         e[cur[u]].c -= aug;
60                         e[cur[u] ^ 1].c += aug;
61                     }
62                     maxf += aug;
63                     aug = inf;
64                 }
65                 goto loop;
66             }
67         }
68         int min_d = n;
69         for(i = head[u]; i != -1; i = e[i].next) {
70             v = e[i].v;
71             if(e[i].c > 0 && dis[v] < min_d) {
72                 min_d = dis[v];
73                 cur[u] = i;                                                     // 与 cur[u]=i(见54行) 对应 ,更新cur[u]的值,使邻接表能够从最近的允许边开始遍历
74             }
75         }
76         if(!(--gap[dis[u]]))                                                    // 出现断层,结束sap
77             break;
78         ++gap[dis[u] = min_d + 1];
79         u = pre[u];
80     }
81     return maxf;
82 }

 建议打印一下,了解各个变量的变化。提供白书上的网络流数据

6 10
5 1 16
5 2 13
1 2 10
2 1 4
1 3 12
2 4 14
3 2 9
4 3 7
4 6 4
3 6 20
5 6

  

 

posted @ 2013-04-14 17:22  Thousand Sunny  阅读(156)  评论(0编辑  收藏  举报