• Day9


    学习网络流中的最大流,自然要先学会基础的最大流算法。这里参考学习挑战写了两个测试代码,学习了通过寻找增广路计算最大流。

     1 /* Ford_Fulkerson算法 
     2  *进行f(最大流)次dfs(), 
     3  * O(f|E|) 
     4 */
     5 #include <bits/stdc++.h>
     6 using namespace std;
     7 const int INF = 0x3f3f3f3f;
     8 const int max_V = 1010;
     9 struct Edge{ //终点,容量, 反向边 
    10     int to, cap, rev;
    11 };
    12 vector<Edge> G[max_V];    //邻接表建图 
    13 bool used[max_V];   //dfs()用到的访问标记
    14 int n, k;
    15 void add_edge(int from, int to, int cap){
    16     G[from].push_back(Edge{to, cap, G[to].size()});
    17     G[to].push_back(Edge{from, 0, G[from].size() - 1});
    18 } 
    19 int dfs(int v, int t, int f){
    20     if(v == t)    return f;
    21     used[v] = true;
    22     for(int i = 0; i<G[v].size(); i++){
    23         Edge &e = G[v][i];
    24         if(!used[e.to] && e.cap>0){
    25             int d = dfs(e.to, t, min(f, e.cap));
    26             if(d > 0){
    27                 e.cap -= d;
    28                 G[e.to][e.rev].cap += d;
    29                 return d;
    30             }
    31         }
    32     }
    33     return 0;
    34 }
    35 int cnt;
    36 int max_flow(int s, int t){
    37     int flow = 0;
    38     while(true){
    39         memset(used, 0, sizeof(used));
    40         int f = dfs(s, t, INF);
    41         
    42         printf("残余网络上的增广路--------- %d
    ", ++cnt);
    43         for(int i=0; i<=n; i++){
    44             for(int j=0; j<G[i].size(); j++){
    45                 printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
    46             }
    47         }    
    48         
    49         if(f == 0){
    50             return flow;
    51         }
    52         flow += f;
    53     }
    54 }
    55 int main(){
    56     freopen("in.txt", "r", stdin);
    57     
    58     for(int i=0; i<n; i++){
    59         G[i].clear();
    60     }
    61     scanf("%d%d", &n, &k);
    62     for(int i=0; i<k; i++){
    63         int fr, to, ca;
    64         scanf("%d%d%d",&fr, &to, &ca);
    65         add_edge(fr, to, ca);
    66     }
    67     for(int i=0; i<=n; i++){
    68         for(int j=0; j<G[i].size(); j++){
    69             printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
    70         }
    71     }
    72     cout << endl; 
    73     printf("%d
    ", max_flow(1, n));
    74     return 0;
    75 }
    76  
    View Code
      1 /*
      2  * Dinic算法
      3  * O(|E||V^2|) 
      4 */
      5 #include <bits/stdc++.h>
      6 using namespace std;
      7 const int INF = 0x3f3f3f3f;
      8 const int max_V = 1010;
      9 struct Edge{ //终点,容量, 反向边 
     10     int to, cap, rev;
     11 };
     12 vector<Edge> G[max_V];
     13 int level[max_V];    //顶点到原点的距离标号
     14 int iter[max_V];    //当前弧,在其之前的边无用
     15 int n, k; 
     16 void add_edge(int from, int to, int cap){
     17     G[from].push_back( (Edge){to, cap, G[to].size()} );
     18     G[to].push_back((Edge){from, 0, G[from].size()-1});     //G[from].size()-1: 为to->from的反向边编号,即边(from->to)的编号 
     19 }
     20 /*
     21  * 通过bfs()计算从源点出发的距离标号 
     22 */
     23 void bfs(int s){
     24     memset(level, -1, sizeof(level));
     25     queue<int> que;
     26     level[s] = 0;
     27     que.push(s);
     28     while(!que.empty()){
     29         int v = que.front();  que.pop();
     30         for(int i=0; i<G[v].size(); i++){
     31             Edge &e = G[v][i];
     32             if(e.cap > 0 && level[e.to] < 0){
     33                 level[e.to] = level[v] + 1;
     34                 que.push(e.to);
     35             }
     36         }
     37     } 
     38 } 
     39 /*
     40  * dfs()寻找增广路 
     41 */
     42 int dfs(int v, int t, int f){
     43     if(v == t)    return f;
     44     for(int &i = iter[v]; i < G[v].size(); i++){
     45         Edge &e = G[v][i];
     46         if(e.cap > 0 && level[v] < level[e.to]){
     47             //level[e.to] == level[v] - 1
     48             int d = dfs(e.to, t, min(f, e.cap));
     49             if(d > 0){
     50                 e.cap -= d;
     51                 G[e.to][e.rev].cap += d;
     52                 return d;
     53             }
     54         }
     55     }
     56     return 0;
     57 }
     58 /*
     59  * 求s到t的最大流 
     60 */
     61 int cnt; ///
     62 int max_flow(int s, int t){
     63     int flow = 0;
     64     while(true){
     65         bfs(s);
     66         if(level[t] < 0)    return flow;
     67         memset(iter, 0, sizeof(iter));
     68         int f;
     69         while((f = dfs(s, t, INF)) > 0){
     70             flow += f;
     71         }
     72         printf("残余网络上的增广路--------- %d
    ", ++cnt);
     73         for(int i=0; i<=n; i++){
     74             for(int j=0; j<G[i].size(); j++){
     75                 printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
     76             }
     77         }
     78         
     79     }
     80 }
     81 int main(){
     82     freopen("in.txt", "r", stdin);
     83     
     84     for(int i=0; i<n; i++){
     85         G[i].clear();
     86     }
     87     scanf("%d%d", &n, &k);
     88     for(int i=0; i<k; i++){
     89         int fr, to, ca;
     90         scanf("%d%d%d",&fr, &to, &ca);
     91         add_edge(fr, to, ca);
     92     }
     93     for(int i=0; i<=n; i++){
     94         for(int j=0; j<G[i].size(); j++){
     95             printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
     96         }
     97     }
     98     cout << endl; 
     99     printf("%d
    ", max_flow(1, n));
    100     return 0;
    101 }
    View Code

    这篇文章对一些概念铺垫的很详细:网络流(一) 入门到熟练

    A -  A Plug for UNIX

    题意:

    n种插座各一个接在电源上,
    m个用电器,每个用电器有一个插座类型,
    k种转换器,可以将一种类型转换为另一种。每种转换器可以用无数个。
    问最少有多少用电器不能用。

    Thinking:

    1. 注意转换器第一个数字是插座类型,提供给用电器,第二个数字是插头类型,提供给插座。
    2. 这里重点是把题目抽象为网络流的最大流问题,题说最少多少电器不能用,则思考最多多少电器能用。难点是如何建图。
    3. 建图:源点到每个插座连一条容量为1的边, 每个插座向每个类型相同的用电器连一条容量为1的边, 每个用电器向汇点连一条容量为1的边,转换器依题目定义连一条INF的边。
      1 #include <cstdio>
      2 #include <algorithm>
      3 #include <cstring>
      4 #include <map>
      5 #include <vector>
      6 #include <queue>
      7 #include <string>
      8 using namespace std;
      9 const int INF = 0x3f3f3f3f;
     10 const int maxn = 820;
     11 struct edge{
     12     int to, cap, rev;
     13 };
     14 vector<edge> G[maxn];
     15 int level[maxn];
     16 int iter[maxn];
     17 void add_edge(int from, int to, int cap){
     18     G[from].push_back((edge){to, cap, G[to].size()});
     19     G[to].push_back((edge){from, 0, G[from].size()-1});
     20 }
     21 //submit时修改下这里,poj不支持这种写法 
     22 void bfs(int s){
     23     memset(level, -1, sizeof(level));
     24     queue<int> que;
     25     level[s] = 0;
     26     que.push(s);
     27     while(!que.empty()){
     28         int v = que.front();   que.pop();
     29         for(int i=0; i<G[v].size(); i++){
     30             edge &e = G[v][i];
     31             if(e.cap > 0 && level[e.to] < 0){
     32                 level[e.to] = level[v] + 1;
     33                 que.push(e.to);
     34             }
     35         }
     36     }
     37 }
     38 int dfs(int v, int t, int f){
     39     if(v == t)    return f;
     40     for(int &i = iter[v]; i<G[v].size(); i++){
     41         edge &e = G[v][i];
     42         if(e.cap > 0 && level[e.to]>level[v]){
     43             int d = dfs(e.to, t, min(f, e.cap));
     44             if(d > 0){
     45                 e.cap -= d;
     46                 G[e.to][e.rev].cap += d;
     47                 return d;
     48             }
     49         }
     50     }
     51     return 0;
     52 }
     53 int max_flow(int s, int t){
     54     int flow = 0;
     55     while(true){
     56         bfs(s);
     57         if(level[t] < 0)    return flow;
     58         memset(iter, 0, sizeof(iter));
     59         int f;
     60         while((f = dfs(s, t, INF)) > 0)        flow += f;
     61     }
     62 }
     63 char str[30], stmp[30];
     64 int cnt;
     65 map<string, int> rec;
     66 int n, m, k;
     67 void init(){
     68     for(int i=0; i<n; i++){
     69         G[i].clear();
     70     }
     71     rec.clear();
     72     cnt = 3;
     73 }
     74 int need[maxn];
     75 int main(){
     76     freopen("in.txt", "r", stdin);
     77     while(scanf("%d", &n) != EOF){
     78         init();
     79         int s = 1, t = 2;
     80         /////
     81         /////建图是重点
     82         /////
     83         /*
     84          * 源点指向插座,容量为1 
     85         */
     86         for(int i=0; i<n; i++){
     87             str[0] = '';
     88             scanf("%s", str);
     89             rec[str] = cnt++;
     90             add_edge(s, rec[str], 1);
     91         }
     92         /*
     93          *插头(str)向其对应的用电器(stmp)指,容量为1
     94          *所有用电器指向源点s,容量为1
     95         */
     96         scanf("%d", &m);
     97         for(int i=0; i<m; i++){
     98             stmp[0] = str[0] = '';
     99             scanf("%s%s",stmp, str);
    100             if(rec.find(str) == rec.end()){
    101                 rec[str] = cnt++;
    102             }
    103             if(rec.find(stmp) == rec.end()){
    104                 rec[stmp] = cnt++;
    105             }
    106             add_edge(rec[stmp], t, 1);
    107             add_edge(rec[str], rec[stmp], 1); 
    108         }
    109         /*
    110          * 可以通过适配器相连的两个插座s1,s2,s2->s1 
    111          * 其值为INF 
    112         */ 
    113         scanf("%d", &k);
    114         for(int i=0; i<k; i++){
    115             stmp[0] = str[0] = '';
    116             scanf("%s%s",stmp, str);
    117             if(rec.find(str) == rec.end()){
    118                 rec[str] = cnt++;
    119             }
    120             if(rec.find(stmp) == rec.end()){
    121                 rec[stmp] = cnt++;
    122             }
    123             add_edge(rec[str], rec[stmp], INF);
    124         }
    125         /* 
    126         for(int i=0; i<cnt; i++){
    127             for(int j=0; j<G[i].size(); j++){
    128                 printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
    129             }
    130         }
    131         cout << endl; 
    132         */ 
    133         printf("%d
    ", m - max_flow(s,t));
    134     }
    135     return 0;
    136 }

    B - Power Network

    题意:一个电力网络,一共n个节点,m条边,np个发电站,nc个用户。每条边有一个容量,每个发电站有一个最大负载,每一个用户也有一个最大接受量。问最多能供给多少电力。

    Thinking:

           属于多个源点和汇点的情况,设一个超级源点,与每一个源点连一条容量为最大负载的边,设一个超级汇点,每一个用户与超级汇点连一条容量为最大接受量的边,然后求最大流。

    这里暴露的问题:

    1. init()函数里用n初始化了,应用maxn。教训:初始化尽量大。
    2. 输入有问题,这里用了cin,可以避免空格的问题。poj discuss里有推荐用scanf(),比cin快了好多。
    1 while(scanf("%c", &tmp)){
    2     if(tmp == '('){
    3         scanf("%d,%d)%d", &u, &v, &z);
    4         break;
    5     }
    6 }
    View Code
      1 #include <cstdio>
      2 #include <iostream>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <map>
      6 #include <vector>
      7 #include <queue>
      8 #include <string>
      9 using namespace std;
     10 const int INF = 0x3f3f3f3f;
     11 const int maxn = 500;
     12 struct edge{
     13     int to, cap, rev;
     14 };
     15 vector<edge> G[maxn];
     16 int level[maxn];
     17 int iter[maxn];
     18 void add_edge(int from, int to, int cap){
     19     G[from].push_back((edge){to, cap, G[to].size()});
     20     G[to].push_back((edge){from, 0, G[from].size()-1});
     21 }
     22 //submit时修改下这里,poj不支持这种写法 
     23 void bfs(int s){
     24     memset(level, -1, sizeof(level));
     25     queue<int> que;
     26     level[s] = 0;
     27     que.push(s);
     28     while(!que.empty()){
     29         int v = que.front();   que.pop();
     30         for(int i=0; i<G[v].size(); i++){
     31             edge &e = G[v][i];
     32             if(e.cap > 0 && level[e.to] < 0){
     33                 level[e.to] = level[v] + 1;
     34                 que.push(e.to);
     35             }
     36         }
     37     }
     38 }
     39 int dfs(int v, int t, int f){
     40     if(v == t)    return f;
     41     for(int &i = iter[v]; i<G[v].size(); i++){
     42         edge &e = G[v][i];
     43         if(e.cap > 0 && level[e.to]>level[v]){
     44             int d = dfs(e.to, t, min(f, e.cap));
     45             if(d > 0){
     46                 e.cap -= d;
     47                 G[e.to][e.rev].cap += d;
     48                 return d;
     49             }
     50         }
     51     }
     52     return 0;
     53 }
     54 int max_flow(int s, int t){
     55     int flow = 0;
     56     while(true){
     57         bfs(s);
     58         if(level[t] < 0)    return flow;
     59         memset(iter, 0, sizeof(iter));
     60         int f;
     61         while((f = dfs(s, t, INF)) > 0)        flow += f;
     62     }
     63 }
     64 void init(){
     65     for(int i=0; i<maxn; i++){
     66         G[i].clear();
     67     }
     68 }
     69 int main(){
     70     freopen("in.txt", "r", stdin);
     71     int n, m, np, nc;
     72     while(cin >> n >> np >> nc >> m){
     73         init();
     74         /////
     75         /////建图是重点
     76         /////
     77         int u, v, z;
     78         char tmp;
     79         /*
     80          * power stations连接 consumers
     81          * 容量为 l(u,v)
     82         */
     83         for(int i = 0; i < m; i++){
     84             cin >> tmp >> u >> tmp >> v >> tmp >> z;
     85         //    printf("%d  %d  %d
    ", u, v, z);
     86             if(u == v){
     87                 continue;
     88 /*
     89  *一个顶点上的环不影响最终结果
     90  *环:一旦u到v后,level数组会标记u,环将无用 
     91 */
     92             }
     93             u++,   v++;
     94             add_edge(u, v, z); 
     95         }
     96         int s = n+1, t = n+2;
     97         /*
     98          * 超级源点s连接 power stations
     99          *  容量为p(u) 
    100         */
    101         for(int i=0; i<np; i++){
    102             cin >> tmp >> u >> tmp >> z;
    103         //    printf("%d  %d
    ", u, z);
    104             u++;
    105             add_edge(s, u, z);
    106         }
    107         /*
    108          * consumers连接超级汇点 t
    109          * 容量为c(u) 
    110         */
    111         for(int i=0; i<nc; i++){
    112             cin >> tmp >> u >> tmp >> z;
    113         //    printf("%d  %d
    ", u, z);
    114             u++;
    115             add_edge(u, t, z);
    116         }
    117         /*
    118         for(int i=0; i<=t; i++){
    119             for(int j=0; j<G[i].size(); j++){
    120                 printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
    121             }
    122         }
    123         cout << endl; 
    124         */
    125         printf("%d
    ", max_flow(s,t));
    126     }
    127     return 0;
    128 }

     C - Ombrophobic Bovines 

    题意:

              有F[1,200]块田地,每块地给出cows的初始值和最终可容纳值[1,1000],集合数量为P[1,1500]的边集(The paths are wide, so that any number of cows can traverse a path in either direction.)注意是双向。求下雨前最短的时间拉警报,使每头牛都可以到达避难所,若所有奶牛都无法进入避难所,则输出-1。

    (感觉每次把题目描述一遍比较好,因为团体赛,和队友交流题意时,感觉彼此都有点儿懵,就当锻炼描述题意能力了)

    Thinking:

           逆着思考,二分答案mid,拆点构建模型,【一般不光边上有容量限制顶点上也有容量限制的情况需要拆点】,这里需要想清楚拆的两点代表什么,怎样与源、汇点相连,因为这决定了如何建田地之间的边,这里建边i->j',可以理解为I的出cow点i到J的收cow点j',容量为无限大,如果其距离小于二分答案。

    这里暴露的问题:

    1. 这里有一个技巧,关于二分时r的设置可以参考poj中的discuss,感觉那里高人好多。
    2. 路径是双向的。
      1 #include <cstdio>
      2 #include <iostream>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <map>
      6 #include <vector>
      7 #include <queue>
      8 #include <string>
      9 using namespace std;
     10 #define LL long long
     11 const LL INFL = 1e16;
     12 const int INF = 0x3f3f3f3f;
     13 const int maxn = 500;
     14 struct edge{
     15     int to, cap, rev;
     16     edge(){}
     17     edge(int a, int b, int c) : to(a), cap(b), rev(c){}
     18 };
     19 vector<edge> G[maxn];
     20 int level[maxn];
     21 int iter[maxn];
     22 void add_edge(int from, int to, int cap){
     23     G[from].push_back(edge(to, cap, G[to].size()));
     24     G[to].push_back(edge(from, 0, G[from].size()-1));
     25 }
     26 void bfs(int s){
     27     memset(level, -1, sizeof(level));
     28     level[s] = 0;
     29     queue<int> que;
     30     que.push(s);
     31     while(!que.empty()){
     32         int v = que.front();  que.pop();
     33         for(int i=0; i<G[v].size(); i++){
     34             edge &e = G[v][i];
     35             if(e.cap>0 && level[e.to]<0){
     36                 level[e.to] = level[v] + 1;
     37                 que.push(e.to);
     38             }
     39         }
     40     }
     41 }
     42 int dfs(int v, int t, int f){
     43     if(v == t)    return f;
     44     for(int &i = iter[v]; i<G[v].size(); i++){
     45         edge &e = G[v][i];
     46         if(e.cap>0 && level[e.to]>level[v]){
     47             int d = dfs(e.to, t, min(e.cap, f));
     48             if(d > 0){
     49                 e.cap -= d;
     50                 G[e.to][e.rev].cap += d;
     51                 return d;
     52             }
     53         }
     54     }
     55     return 0;    
     56 }
     57 int max_flow(int s, int t){
     58     int flow = 0;
     59     while(true){
     60         bfs(s);
     61         if(level[t] < 0)    return flow;
     62         memset(iter, 0, sizeof(iter));
     63         int f;
     64         while((f=dfs(s, t, INF)) > 0)    flow += f;
     65     }
     66 }
     67 LL dist[maxn][maxn];
     68 void floyd(int n){
     69     for(int k=1; k<=n; k++){
     70         for(int i=1; i<=n; i++){
     71             for(int j=1; j<=n; j++){
     72                 dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
     73             }
     74         }
     75     }
     76 }
     77 int init_cows[maxn], hold_cows[maxn];
     78 void buildG(LL mid, int n, int s, int t){
     79     for(int i=0; i<maxn; i++){
     80         G[i].clear();
     81     }
     82     for(int i=1; i<=n; i++){
     83         add_edge(s, i, init_cows[i]);
     84         add_edge(i+n, t, hold_cows[i]);
     85         add_edge(i, i+n, INF);        
     86     }
     87     /*
     88      * 拆点 
     89      *  怎么确定出入顺序
     90      *  这里是建图的重点 
     91      * 可以理解为从 i的出牛点 到 j收牛点 可以有INF的容量 
     92     */
     93     for(int i=1; i<=n; i++){
     94         for(int j=i+1; j<=n; j++){
     95             if(dist[i][j] <= mid){
     96                 add_edge(i, j+n, INF);
     97                 add_edge(j, i+n, INF);
     98             }
     99         }
    100     }    
    101 } 
    102 int main(){
    103 //    freopen("in.txt", "r", stdin);
    104     int n, m;
    105     while(scanf("%d%d", &n, &m) != EOF){
    106         int s = 0, t = 2*n+1, sum = 0;
    107         for(int i=1; i<=n; i++){
    108             scanf("%d%d", &init_cows[i], &hold_cows[i]);
    109             sum += init_cows[i];
    110         }
    111         for(int i=1; i<=n; i++)
    112             for(int j=1; j<=n; j++)
    113                 dist[i][j] = INFL;
    114     /* 这里是求解的重点 二分的运用+如何将问题转换为最大流
    115      *       ????为什么这么思考我还是没想通, 
    116      * floyd()求得任意两点间的最短路径,
    117      * 然后二分答案 
    118      * 判断是否可以在mid的距离(时间)内完成移动
    119      * 即max_flow() >= sum 
    120     */
    121         LL l = 0, r = 1, ans = -1;
    122 /*
    123  * 这里关于r的优化方法是看的poj discuss,发现里面很多讨论还是很独到的。
    124  * 最开始我设置了 "how long any cow takes to traverse it "的最大值 与 P的最大值的积
    125  * 即1e9 * 1500 * 10, 显然时间快了200-350
    126 */
    127         for(int i=0; i<m; i++){
    128             int x, y;
    129             LL val;
    130             scanf("%d%d%lld", &x, &y, &val);
    131             if(val < dist[x][y])
    132                 dist[y][x] = dist[x][y] = val;
    133             //这里路径是双向的
    134             r += val;
    135         }
    136         floyd(n);
    137         while(l <= r){
    138             LL mid = (l+r)/2;
    139             buildG(mid, n, s, t);
    140             int tmp = max_flow(s, t);
    141             if(tmp >= sum){
    142                 ans = mid;
    143                 r = mid -1;
    144             }else{
    145                 l = mid + 1;
    146             }
    147         }
    148         /*
    149         for(int i=0; i<=t; i++){
    150             for(int j=0; j<G[i].size(); j++){
    151                 printf("[%d %d  %d  %d]%s", i, G[i][j].to, G[i][j].cap, G[i][j].rev, j==(G[i].size()-1)?"
    ":"   ");
    152             }
    153         }
    154         cout << endl;
    155         */
    156         printf("%lld
    ", ans); 
    157     }
    158     return 0;
    159 }

     D -  Secret Milking Machine

    题意:

    N(2,200)个landmarks,P(1,40000)条bidirectional trails(双向路径),从1到N,使T条不同的路径中所有边中最长的边最小,注意不是边之和路径最小,而是边最小。

    Thinking:

    1. 因为T条路径不存在同边,显然这里建图时符合条件的边的容量为1(保证不同路径不会同边),其余为0,回边与边的容量相同。
    2. 二分答案(边的长度),按1的思路建图,条件显然为边长小于mid时cap为1。然后求最大流,看是否有T条路径。

    注意:图中有平行边和重边,这个不能随便处理,因为是求解最大边的最小值。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <vector>
      5 #include <queue>
      6 using namespace std;
      7 #define LL long long
      8 const int INF = 0x3f3f3f3f;
      9 const int maxn = 410, maxp = 40010;
     10 struct edge{
     11     int to, cap, rev;
     12     edge(){}
     13     edge(int a, int b, int c) : to(a), cap(b), rev(c) {}
     14 };
     15 vector<edge> G[maxn];
     16 int level[maxn], iter[maxn];
     17 void add_edge(int from, int to, int cap){
     18     G[from].push_back(edge(to, cap, G[to].size()));
     19     G[to].push_back(edge(from, cap, G[from].size()-1));
     20 }
     21 void bfs(int s){
     22     memset(level, -1, sizeof(level));
     23     level[s] = 0;
     24     queue<int> que;
     25     que.push(s);
     26     while(!que.empty()){
     27         int v = que.front();  que.pop();
     28         for(int i=0; i<G[v].size(); i++){
     29             edge &e = G[v][i];
     30             if(e.cap > 0 && level[e.to] < 0){
     31                 level[e.to] = level[v] + 1;
     32                 que.push(e.to);
     33             }
     34         } 
     35     }
     36 }
     37 int dfs(int v, int t, int f){
     38     if(v == t)    return f;
     39     for(int &i = iter[v]; i < G[v].size(); i++){
     40         edge &e = G[v][i];
     41         if(e.cap > 0 && level[e.to] > level[v]){
     42             int d = dfs(e.to, t, min(f, e.cap));
     43             if(d > 0){
     44                 e.cap -= d;
     45                 G[e.to][e.rev].cap += d;
     46                 return d;
     47             }
     48         }
     49     }
     50     return 0;
     51 }
     52 int max_flow(int s, int t){
     53     int flow = 0;
     54     while(true){
     55         bfs(s);
     56         if(level[t] < 0)    return flow;
     57         memset(iter, 0, sizeof(iter));
     58         int f;
     59         while( (f = dfs(s, t, INF)) > 0){
     60             flow += f;
     61         }
     62     }
     63 }
     64 int A[maxp], B[maxp], L[maxp];
     65 void buildG(int n, int mid){
     66     for(int i=0; i<maxn; i++)    G[i].clear();
     67     for(int i=0; i<n; i++){
     68         if(L[i] <= mid){
     69             add_edge(A[i], B[i], 1);
     70         }
     71     }
     72 }
     73 int main(){
     74 //    freopen("in.txt", "r", stdin);
     75     int n, p, t;
     76     while( scanf("%d%d%d", &n, &p, &t) != EOF ){
     77         /*
     78          * discuss里说这里取上界可能会超时
     79          * 关于二分的l,r取值不能太随便 
     80         */
     81         int l = INF, r = 0;
     82         /*
     83          * 注意这里有平行边
     84          * 想了想,这里不能随便处理,因为二分的是 最长 边的 最小 值 
     85         */ 
     86         for(int i=0; i<p; i++){
     87             scanf("%d%d%d", &A[i], &B[i], &L[i]);
     88             l = min(l, L[i]);
     89             r = max(r, L[i]);
     90         }
     91         int ans = -1;
     92         while(l <= r){
     93             int mid = (l + r)/2;
     94             buildG(p, mid);
     95             if(max_flow(1, n) >= t){
     96                 ans = mid;
     97                 r = mid - 1;
     98             }else{
     99                 l = mid + 1;
    100             }
    101         }
    102         printf("%d
    ", ans);
    103     }
    104     return 0;
    105 }

    下面题目是有关最小费用最大流。

    这里放上文章,但还未细看。1、最小费用最大流(详解+模板)   2、最详细网络流建模基础

    E - Farm Tour

    题意:农场N[1,1000]块地,M[1,10000]条双向道路,ai-->bi,长ci,问从1-->N,再从N-->1,往返道路无同边,求总长度最短。

    Thinking: 

    这里建图与D题有异曲同工之处,因为是来回不同边,所以可以视为从1-->N最大流为2,且每条边容量为1。然后求最小费用流。

    这里代码是学习挑战这本书的,还不是很理解这个算法,回头再看。

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <vector>
     4 #include <cstring>
     5 using namespace std;
     6 #define LL long long
     7 const int INF = 0x3f3f3f3f;
     8 const int maxn = 1010;
     9 struct edge{
    10     int to, cap, cost, rev;
    11     edge(int a, int b, int c, int d) : to(a), cap(b), cost(c), rev(d){}
    12 };
    13 int V;    //顶点数 
    14 vector<edge> G[maxn];
    15 int dist[maxn];        //最短距离 
    16 int prevv[maxn], preve[maxn];        //最短路的前驱结点和对应的边
    17 void add_edge(int from, int to, int cap, int cost){
    18     G[from].push_back(edge(to, cap, cost, G[to].size()));
    19     G[to].push_back(edge(from, 0, -cost, G[from].size()-1));
    20 }
    21 //求解从s到t流量为f的最小费用流
    22 //如果不能再增广,返回-1
    23 int min_cost_flow(int s, int t, int f){
    24     int res = 0;
    25     while(f > 0){
    26         fill(dist, dist+V, INF);
    27         dist[s] = 0;
    28         bool update = true;
    29         while(update){
    30             update = false;    
    31             for(int v = 0; v < V; v++){
    32                 if(dist[v] == INF){
    33                     continue;
    34                 }
    35                 for(int i = 0; i < G[v].size(); i++){
    36                     edge &e = G[v][i];
    37                     if(e.cap > 0 && dist[e.to] > dist[v]+e.cost){
    38                         dist[e.to] = dist[v] + e.cost;
    39                         prevv[e.to] = v;
    40                         preve[e.to] = i;
    41                         update = true;
    42                     }
    43                 }
    44             }
    45         }
    46         if(dist[t] == INF){
    47             return -1;
    48         }//不能增广
    49         //沿s->t的最短路尽量增广
    50         int d = f;
    51         for(int v = t; v != s; v = prevv[v]){
    52             d = min(d, G[prevv[v]][preve[v]].cap);
    53         } 
    54         f -= d;
    55         res += d * dist[t];
    56         for(int v = t; v != s; v=prevv[v]){
    57             edge &e = G[prevv[v]][preve[v]];
    58             e.cap -= d;
    59             G[v][e.rev].cap += d;
    60         }
    61     }
    62     return res;
    63 }
    64 int main(){
    65 //    freopen("in.txt", "r", stdin);    
    66     int n, m;
    67     while( scanf("%d%d", &n, &m) != EOF ){
    68         for(int i=0; i<maxn; i++){
    69             G[i].clear();
    70         }
    71         for(int i = 0; i < m; i++){
    72             int x, y, z;
    73             scanf("%d%d%d", &x, &y, &z);
    74             add_edge(x-1, y-1, 1, z);
    75             add_edge(y-1, x-1, 1, z);
    76         }
    77         int s = 0, t = n-1;
    78         V = n;
    79         printf("%d
    ", min_cost_flow(s, t, 2));
    80     }
    81     return 0;
    82 } 
  • 相关阅读:
    81. Search in Rotated Sorted Array II
    33. Search in Rotated Sorted Array
    26. Remove Duplicates from Sorted Array
    977. Squares of a Sorted Array
    碎片知识
    boost::shared_ptr
    faster-rcnn代码阅读-训练整体流程
    faster-rcnn代码阅读-数据预处理
    进栈与出栈
    jupyter-notebook添加python虚拟环境的kernel
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9452470.html
Copyright © 2020-2023  润新知