网络流初步--最大流

  网络流 (Network Flow) 是指在一个每条边都有容量 (Capacity)有向图分配使一条边的流量不会超过它的容量。(边有附带容量的图称为网络)一道流必须符合一个结点的进出的流量相同的限制,除非这是一个源点 (Source) ──有较多向外的流;或是一个汇点 (Sink) ──有较多向内的流。一个网络可以用来模拟道路系统的交通量、管中的液体、电路中的电流或类似一些东西在一个结点的网络中游动的任何事物。

  G(V, E) 是一个有限的有向图,它的每条边 (u, v) \in E 都有一个非负值实数的容量 c(u, v) 。如果 (u, v) \not \in E ,我们假设 c(u, v) = 0 。我们区别两个顶点:一个源点 s 和一个汇点 t。一道网络流是一个对于所有结点 uv 都有以下特性的实数函数 f:V \times V \rightarrow \mathbb{R}

容量限制 (Capacity Constraints) \ f(u, v) \le c(u, v) 一条边的流不能超过它的容量。
斜对称 (Skew Symmetry) \ f(u, v) = - f(v, u)uv 的净流必须是由 vu 的净流的相反(参考例子)。
流守恒 (Flow Conservation) 除非u = su = t,否则\ \sum_{w \in V} f(u, w) = 0 一结点的净流是零,除了“制造”流的源点和“消耗”流的汇点

  边的剩余容量 (Residual Capacity)c_f(u, v) = c(u, v) - f(u, v)。这定义了以 G_f(V, E_f) 表示的剩余网络 (Residual Network),它显示可用的容量的多少。留意就算在原网络中由 uv 没有边,在剩余网络仍可能有由 uv 的边。因为相反方向的流抵消,减少vu 的流等于增加uv 的流增广路 (Augmenting Path) 是一条路径 (u_1, u_2, \dots, u_k),而 u_1 = su_k = tc_f(u_i, u_{i+1}) > 0,这表示沿这条路径传送更多流是可能的(既找一条从s到t的路径,路径上边的剩余容量都不为0)。

 【增广路求最大流算法】

  在网络中找到一条增广路,路径上剩余容量最小的为d,把路径上所有边的流量都+d,显然操作后依旧满足上面的3条性质。只要网络中存在增广路,网络的流量就可以增加。如果不存在增广路,则当前流是最大流。

【Edmonds-Karp】

  这个算法写的比较简洁,是用BFS来找增广路径的,防止一些数据会卡DFS。
       算法复杂度为O(VE2),适合节点多边少的网络,如果要求解边多的网络,则要用Dinic算法: O(V2E)

 

 1 /*
 2     数组a[i]表示从路径起点到i点的最小剩余量
 3     数组p[u]记录路径中到达u点的前一个点
 4     f[u][v]表示从u到v的实际流量,要保证当前网络中的流量满足3个性质才能执行算法,最好都清0吧。
 5 */   
 6     queue<int> q;
 7     int sum=0;
 8     while (true)
 9     {
10        memset(a,0,sizeof a);
11        a[s]=MAXINT;  //s为起点,初始化防止被更新
12        q.push(s);
13        while (!q.empty())
14        {
15            int u=q.front();q.pop();
16            for (int v=1;v<=n;++v)
17             if (!a[v] && c[u][v]>f[u][v])
18            {
19                p[v]=u;  q.push(v);
20                int t=c[u][v]-f[u][v];
21                a[v]=a[u]<t?a[u]:t;
22            }
23        }
24        if (a[t]==0) break;
25        for (int u=t;u!=s;u=p[u])
26        {
27            f[p[u]][u]+=a[t];
28            f[u][p[u]]-=a[t];
29        }
30         sum+=a[t];   //更新网络总流量
31     }

 

  代码中的BFS部分有点意思,由于数组a总是正数(c[u][v]>f[u][v]),所以可以当做标记是否该点被访问过。BFS中所有点只被更新一次,如果第一次访问该点无法到达t,则再多次访问也没用,所以效率还是蛮高的。

【HDU 3549】

  最大流的最基本题。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <queue>
 4 using namespace std;
 5 int n,m;
 6 int c[20][20];
 7 int f[20][20];
 8 int work()
 9 {
10     int p[50];
11     queue<int> q;
12     int sum=0;
13     int a[20];
14     while (true)
15     {
16        memset(a,0,sizeof a);
17        a[1]=2000000;
18        q.push(1);
19        while (!q.empty())
20        {
21            int u=q.front();q.pop();
22            for (int v=1;v<=n;++v)
23             if (!a[v] && c[u][v]>f[u][v])
24            {
25                p[v]=u;q.push(v);
26                int t=c[u][v]-f[u][v];
27                a[v]=a[u]<t?a[u]:t;
28            }
29        }
30        if (a[n]==0) break;
31        for (int u=n;u!=1;u=p[u])
32        {
33            f[p[u]][u]+=a[n];
34            f[u][p[u]]-=a[n];
35        }
36         sum+=a[n];
37     }
38     return sum;
39 }
40 int main()
41 {
42     int T;
43     int cas=0;
44     scanf("%d",&T);
45     while (T--)
46     {
47         memset(c,0,sizeof c);
48         memset(f,0,sizeof f);
49         scanf("%d%d",&n,&m);
50         for (int i=0;i<m;++i)
51         {
52             int x,y,t;
53             scanf("%d%d%d",&x,&y,&t);
54             c[x][y]+=t;
55         }
56         printf("Case %d: %d\n",++cas,work());
57     }
58 }

 用最大流模板的代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <queue>
  4 using namespace std;
  5 int n,m;
  6 class nstream
  7 {
  8     static const int MAXINT=0x7fffffff;
  9     struct node
 10     {
 11         int n,c,f;
 12         int next;
 13         void set(int nn,int cc,int xx)
 14         {
 15             n=nn;   c=cc;
 16             next=xx;   f=0;
 17         }
 18     } *data;
 19     int *head,cnt,n,m;
 20     int *getf(int x,int y)
 21     {
 22         int ind=head[x];
 23         for (node *t; ind!=-1; ind=t->next)
 24         {
 25             t=&data[ind];
 26             if (t->n==y) return &t->f;
 27         }
 28         insert(x,y,0);
 29         return &data[head[x]].f;
 30     }
 31 public:
 32     nstream(int nn,int mm):n(nn),m(mm)  //输入最大节点数和边数
 33     {
 34         data=new node[mm];
 35         head=new int[nn+2];
 36     }
 37     void clear()      //清理数据,初始化
 38     {
 39         for (int i=0;i<=n;++i) head[i]=-1;
 40         cnt=-1;
 41     }
 42     void insert(int x,int y,int k) // 把x到y的容量加k
 43     {
 44         int ind=head[x];
 45         for (node *t; ind!=-1; ind=t->next)
 46         {
 47             t=&data[ind];
 48             if (t->n==y)
 49             {
 50                 t->c+=k;
 51                 return;
 52             }
 53         }
 54         data[++cnt].set(y,k,head[x]);
 55         head[x]=cnt;
 56     }
 57     int work(int s,int t)  //计算从s 到t 的最大流
 58     {
 59         int p[50];
 60         queue<int> q;
 61         int sum=0;
 62         int a[25];
 63         while (true)
 64         {
 65             memset(a,0,sizeof a);
 66             a[s]=MAXINT;
 67             q.push(s);
 68             while (!q.empty())
 69             {
 70                 int u=q.front();
 71                 q.pop();
 72                 node *t;
 73                 for (int pp=head[u]; pp!=-1; pp=t->next)
 74                 {
 75                     t=&data[pp];
 76                     int v=t->n;
 77                     if (!a[v] && t->c>t->f)
 78                     {
 79                         p[v]=u;
 80                         q.push(v);
 81                         int tt=t->c-t->f;
 82                         a[v]=a[u]<tt?a[u]:tt;
 83                     }
 84                 }
 85             }
 86             if (a[t]==0) break;
 87             for (int u=t; u!=s; u=p[u])
 88             {
 89                 (*getf(p[u],u))+=a[t];
 90                 (*getf(u,p[u]))-=a[t];
 91             }
 92             sum+=a[t];
 93         }
 94         return sum;
 95     }
 96 };
 97 
 98 int main()
 99 {
100     int T;
101     int cas=0;
102     scanf("%d",&T);
103     nstream my(20,20*23);
104     while (T--)
105     {
106         my.clear();
107         scanf("%d%d",&n,&m);
108         for (int i=0; i<m; ++i)
109         {
110             int x,y,t;
111             scanf("%d%d%d",&x,&y,&t);
112             my.insert(x,y,t);
113         }
114         printf("Case %d: %d\n",++cas,my.work(1,n));
115     }
116 }

 

 

posted @ 2013-03-21 10:19  wuminye  阅读(523)  评论(0编辑  收藏  举报