zoukankan      html  css  js  c++  java
  • 【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93

    最大流首次体验感受——

     

    什么是最大流呢?

    从一个出发点(源点),走到一个目标点(汇点),途中可以经过若干条路,每条路有一个权值,表示这条路可以通过的最大流量。

    最大流就是从源点到汇点,可以通过的最大流量。

    接下来我们看一个图——

    图1

    这个图中,s是源点,t是汇点。期间可以经过2, 3, 4, 5, 6几个点。每条边上有两个权值,其中第一个表示当前通过这条边的流量,第二个表示这条边最大可以通过的流量。

    最佳情况,即最大可以通过的流量的一种情况是这样的——

    图2

    但还有一种情况,同样可以达到最大流——

    图3

    这里得到两个结论:

    1. 每条路径中都有至少一条边是满的。

    2. 最大流可能不止一种情况。

     

    接下来我们再看另一个图:

    图4

    如果在这个图上找最大流该怎么找?

    这样?

    图5

    不对,这个图乍一看好像满足每条路径上都有一个满流的边这个条件,但是其实还有更大的流——

    图6

    怎么办呢?

    我们可以通过这个方式从图5变到图6——

    图7

    这里我们的可以这样理解,在我们走出图5 的结果以后,我们允许图中出现图7中的绿色的边,然后我们就得到了绿色的数字所标示出的一条新路,通过这条路径,我们就获得了最大流。

    如果我们获得了一个流量图,这个流量图中每条路径上都有一条边是满流了,如何判断这是不是一个最大流的图呢?通过上面的方法,我们在通过某条边之后,在这两个点之间构造一条反向的并且和通过的流量大小相同的边(称为反向边)。这样,就可能产生一条新路,使整个图中的流量增加。那么,我们不断地构造这种边,直到无法寻找到新的路径为止(称为增广路径),是不是就得到了最大流呢?

    总结起来,每次找到一条增广路,增广路中每条边的值,都减去路径中,边值最小的边的值(读起来很凹口是不是?多读几遍就好了)。同时,还要给每条边都加上反向边。重复寻找,直到找不到新的路径,我们就获得了这个图的最大流。

    注意,

    1. 每次寻找增广路径后,我们都会将原图更改,这样,我们会得到一个新的图。
    2. 在获得最大流的图之前,我们获得的每张图都称为残余网络。原始图也可以视为残余网络。

    以上讲的是寻找最大流的思想。

    但是,寻找增广路径的方法不止一种。

    我最直接想到的方法,使用dfs多次搜索这张图,直到找不到为止。

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 const int N = 210;
     8 const int M = 10000010;
     9 
    10 int n, m;
    11 int mp[N][N];               //保存地图信息,有向图
    12 bool vis[N][N];             //dfs时标记使用
    13 int ans;                    //最终结果
    14 
    15 void init()
    16 {
    17     memset(mp, 0, sizeof(mp));
    18     memset(vis, 0, sizeof(vis));
    19     for(int i = 0; i < n; i++)
    20     {
    21         int a, b, c;
    22         scanf("%d%d%d", &a, &b, &c);
    23         mp[a][b] += c;
    24     }
    25     ans = 0;
    26 }
    27 
    28 int dfs(int x, int maxn)
    29 {
    30     if(x == m)
    31     {
    32         ans += maxn;        //每次走到汇点后增加的流量
    33         return maxn;
    34     }
    35     int flow = 0;
    36     for(int i = 1; i <= m; i++)
    37     {
    38         if(mp[x][i] > 0 && !vis[x][i])
    39         {
    40             vis[x][i] = 1;
    41             int mmaxn = maxn < mp[x][i] ? maxn : mp[x][i];      //取本路径中所可以通过的最小值
    42             int mid;
    43             if(mmaxn > 0) mid = dfs(i, mmaxn);                  //如果此路仍然是通路,则继续搜索
    44             if(mid > 0)
    45             {
    46                 mp[x][i] -= mid;                        //已经经过的路要减去耗费的流量
    47                 mp[i][x] += mid;                        //反向路(弧)增加耗费的流量
    48                 maxn -= mid;                            //走过一条通路后剩余的流量
    49                 flow += mid;                            //已经消耗的流量
    50                 if(maxn == 0) break;
    51             }
    52         }
    53     }
    54     return flow;
    55 }
    56 
    57 int main()
    58 {
    59     //freopen("test.in", "r", stdin);
    60     while(~scanf("%d%d", &n, &m))
    61     {
    62         init();
    63         while(dfs(1, M) > 0) memset(vis, 0, sizeof(vis));
    64         printf("%d
    ", ans);
    65     }
    66     return 0;
    67 }
    dfs

    但是后来我突然想到。dfs找到的不一定最短路,每次搜索可能会浪费时间,然后又改成了bfs,这种方法也就是常说的Edmonds-Karp算法。

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <queue>
     6 using namespace std;
     7 
     8 const int N = 210;
     9 const int M = 100000010;
    10 
    11 int mp[N][N];
    12 int fm[N];              //用于记录路径
    13 int val[N];
    14 int n, m;
    15 int ans;
    16 
    17 void init()
    18 {
    19     memset(mp, 0, sizeof(mp));
    20     for(int i = 0; i < n; i++)
    21     {
    22         int a, b, c;
    23         scanf("%d%d%d", &a, &b, &c);
    24         mp[a][b] += c;
    25     }
    26     ans = 0;
    27 }
    28 
    29 void work()
    30 {
    31     while(1)
    32     {
    33         memset(val, 0, sizeof(val));
    34         val[1] = M;
    35         queue<int> que;
    36         que.push(1);
    37         while(!que.empty())             //spfa,寻找最短路
    38         {
    39             int k = que.front();
    40             que.pop();
    41             if(k == m) break;
    42             for(int i = 1; i <= m; i++)
    43             {
    44                 if(!val[i] && mp[k][i] > 0)
    45                 {
    46                     fm[i] = k;
    47                     que.push(i);
    48                     val[i] = val[k] < mp[k][i] ? val[k] : mp[k][i];
    49                 }
    50             }
    51         }
    52         if(val[m] == 0) break;                  //当前图上找不到源点到汇点的通路,则退出
    53 
    54         for(int i = m; i != 1; i = fm[i])
    55         {
    56             mp[fm[i]][i] -= val[m];             //经过的路径上要减去耗费的流量
    57             mp[i][fm[i]] += val[m];             //反向路径(弧)增加相应的路径
    58         }
    59         //printf("%5d
    ", val[m]);
    60         ans += val[m];                          //结果增加新增的流量
    61     }
    62 }
    63 
    64 void outit()
    65 {
    66     printf("%d
    ", ans);
    67 }
    68 
    69 int main()
    70 {
    71     while(~scanf("%d%d", &n, &m))
    72     {
    73         init();
    74         work();
    75         outit();
    76     }
    77     return 0;
    78 }
    bfs

     

    接下来又找到了一种看起来很高大上的方法——Dinic算法。

    这个方法要说一说,因为我也花了不少时间来理解,虽然还没有完全理解,但是已经被它所包含的思想震撼了。

    这个算法是一层一层搜索的。简单来说,就是:

    1)将这个图中用bfs遍历一遍,严格确立每个点的层次。

    如果使用bfs可以从源点走到汇点,那么执行2),否则这张图中不存在新的增广路,算法结束。

    2)从源点开始dfs,寻找到当前图中所有从源点到汇点的路径,在寻找时,严格按照点的层次寻找,只能从第i层的点走到第i+1层的点。

    3)重复1)。

    好神奇的方法。

      1 #include <cstdio>
      2 #include <cmath>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <queue>
      6 using namespace std;
      7 
      8 const int N = 210;
      9 const int M = 100000010;
     10 
     11 int mp[N][N];
     12 int dis[N];
     13 int cur[N];
     14 bool vis[N];
     15 int n, m;
     16 int ans;
     17 
     18 void init()
     19 {
     20     memset(mp, 0, sizeof(mp));
     21     for(int i = 0; i < n; i++)
     22     {
     23         int a, b, c;
     24         scanf("%d%d%d", &a, &b, &c);
     25         mp[a][b] += c;
     26     }
     27     ans = 0;
     28 }
     29 
     30 bool bfs()
     31 {
     32     memset(vis, 0, sizeof(vis));
     33     queue<int> que;
     34     que.push(1);
     35     dis[1] = 0;
     36     vis[1] = 1;
     37     while(!que.empty())
     38     {
     39         int k = que.front();
     40         que.pop();
     41         for(int i = 1; i <= m; i++)
     42         {
     43             if(!vis[i] && mp[k][i] > 0)
     44             {
     45                 vis[i] = 1;
     46                 dis[i] = dis[k]+1;
     47                 que.push(i);
     48             }
     49         }
     50     }
     51     return vis[m];
     52 }
     53 
     54 int dfs(int x, int val)
     55 {
     56     if(x == m) return val;
     57     int flow = 0, minn;
     58     for(int& i = cur[x]; i <= m; i++)       //随着i的变化改变cur[x],这样可以节省当前图中下次使用x时耗费的时间
     59     {
     60         int mval = val < mp[x][i] ? val : mp[x][i];     //记录当前路径中的最小的边的权,最后要根据它建立反向边
     61         if(dis[x]+1 == dis[i])
     62         {
     63             minn = 0;
     64             if(mval > 0) minn = dfs(i, mval);
     65             if(minn > 0)
     66             {
     67                 mp[x][i] -= minn;
     68                 mp[i][x] += minn;
     69                 flow += minn;
     70                 val -= minn;
     71                 if(val == 0) break;
     72             }
     73         }
     74 
     75     }
     76     return flow;
     77 }
     78 
     79 void work()
     80 {
     81     while(bfs())        //如果存在增广路,则dfs寻找,否则结束
     82     {
     83         for(int i = 1; i <= m; i++) cur[i] = 1;
     84         ans += dfs(1, M);
     85     }
     86 }
     87 
     88 void outit()
     89 {
     90     printf("%d
    ", ans);
     91 }
     92 
     93 int main()
     94 {
     95     //freopen("test.in", "r", stdin);
     96     while(~scanf("%d%d", &n, &m))
     97     {
     98         init();
     99         work();
    100         outit();
    101     }
    102     return 0;
    103 }
    Dinic
  • 相关阅读:
    数据库隔离级别
    Mysql 命令详解
    Mysql 索引
    强化学习(四):蒙特卡洛方法
    强化学习(三):动态编程
    强化学习(二):马尔可夫决策过程
    强化学习(一): 引入
    自然语言处理(五)时下流行的生成模型
    论文选读三 QANet
    皮质学习 HTM 知多少
  • 原文地址:https://www.cnblogs.com/mypride/p/4859453.html
Copyright © 2011-2022 走看看