zoukankan      html  css  js  c++  java
  • 网络流初步最大流

      网络流 (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 }
  • 相关阅读:
    王健林:在中国远离政府太假了 期望王思聪稳重
    科目二很难考吗?经验全在这里!
    HTTP 的长连接和短连接
    JS中实现字符串和数组的相互转化
    Maven介绍,包括作用、核心概念、用法、常用命令、扩展及配置
    kafka数据可靠性深度解读
    深入浅出JMS(二)--ActiveMQ简单介绍以及安装
    ActiveMQ入门实例
    activemq的几种基本通信方式总结
    mysql按年度、季度、月度、周、日SQL统计查询
  • 原文地址:https://www.cnblogs.com/wuminye/p/2972690.html
Copyright © 2011-2022 走看看