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

    题目描述

    如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

    输入输出格式

    输入格式:

    第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

    接下来M行每行包含三个正整数ui、vi、wi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi)

    输出格式:

    一行,包含一个正整数,即为该网络的最大流。

    输入输出样例

    输入样例#1: 复制
    4 5 4 3
    4 2 30
    4 3 20
    2 3 20
    2 1 30
    1 3 40
    输出样例#1: 复制
    50

    说明

    时空限制:1000ms,128M

    数据规模:

    对于30%的数据:N<=10,M<=25

    对于70%的数据:N<=200,M<=1000

    对于100%的数据:N<=10000,M<=100000

    样例说明:

    题目中存在3条路径:

    4-->2-->3,该路线可通过20的流量

    4-->3,可通过20的流量

    4-->2-->1-->3,可通过10的流量(边4-->2之前已经耗费了20的流量)

    故流量总计20+20+10=50。输出50。

    原本不打算看网络流,下午帮沈爷爷debug的时候顺便学了一下。

    这里写的是DINIC算法。

    大概分为BFS 和 DFS 两部分。

    BFS主要是用来给图来分层,就是存一下每个点的深度。

    代码如下:

     1 queue<ll>qq;
     2 bool bfs() {
     3 
     4     while(!qq.empty()) qq.pop();
     5     memset(dep,0,sizeof(dep));
     6     dep[s] = 1;
     7     qq.push(s);
     8     while(!qq.empty()) {
     9         ll u = qq.front();
    10         qq.pop();
    11         for(int i = head[u]; ~i ; i = e[i].nxt) {
    12             if(e[i].c and dep[e[i].v] == 0) {
    13                 dep[e[i].v] = dep[u] + 1;
    14                 qq.push(e[i].v);
    15             }
    16         }
    17     }
    18     return dep[t];
    19 }

    而DFS就是来寻找增广路了。就是从源点来走,尽量将所有能走的边都走完。

    代码如下:

     1 ll dfs(ll nd, ll val) {
     2     if(val == 0) return 0;
     3     if(nd == t) return val;
     4     for(int i = head[nd] ; ~i ; i = e[i].nxt) {
     5         ll u = e[i].v;
     6         if(dep[u] == dep[nd] + 1 and e[i].c) {
     7             ll k = dfs(u , min(e[i].c,val));
     8             if(k) {
     9                 e[i].c -= k;
    10                 e[i ^ 1].c += k;
    11                 return k;
    12             }
    13 
    14         }
    15     }
    16     return 0;
    17 }

    经过我一下午的debug经验,这里重点强调一个问题

    存反向边的时候,因为我们存边的时候是需要编号的,而且我们需要尽量快的找到反向边。所以我们在这里设e [ i ]为原有流量,设e [ i ^  1 ] 为反向边的流量。

    但是这里就有问题了。如果我们第一个存下的边的编号设为 1 的话,那我们第一条边的反向边就是0了,而我们这样就返回不到第0条边。

    同样,我们在遍历边的时候,我们就不能写成 for ( int i = head[ u ] ; i ; i = e[ i ]. nxt)了;

    就必须写成for(  ; i>=0 ;  ) 或者 for(  ;  ~i  ;   )。

    同样我们在存边的时候需要预处理一下head数组,初值最好设为-1, 否则会死循环。

    完整代码如下:

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<vector>
     6 #include<queue>
     7 #include <iostream>
     8 using namespace std;
     9 typedef long long ll;
    10 const ll maxn = 1e6 + 7;
    11 #define inf 2147483647
    12 ll read() {
    13     ll a = 0, b = 1;
    14     char c = getchar();
    15     while(c < '0' or c > '9') {
    16         if(c == '-') b = -1;
    17         c = getchar();
    18     }
    19     while(c >= '0' and c <= '9') {
    20         a = a * 10 + c - '0';
    21         c = getchar();
    22     }
    23     return a * b;
    24 }
    25 ll dep[maxn],w[maxn],cnt = -1,n,m,s,ans,t,head[maxn];
    26 struct node {
    27     ll v,c,nxt;
    28 } e[maxn];
    29 void add(ll ai, ll bi, ll ci) {
    30     e[++cnt].v = bi;
    31     e[cnt].c = ci;
    32     e[cnt].nxt = head[ai];
    33     head[ai] = cnt;
    34 }
    35 queue<ll>qq;
    36 bool bfs() {
    37 
    38     while(!qq.empty()) qq.pop();
    39     memset(dep,0,sizeof(dep));
    40     dep[s] = 1;
    41     qq.push(s);
    42     while(!qq.empty()) {
    43         ll u = qq.front();
    44         qq.pop();
    45         for(int i = head[u]; ~i ; i = e[i].nxt) {
    46             if(e[i].c and dep[e[i].v] == 0) {
    47                 dep[e[i].v] = dep[u] + 1;
    48                 qq.push(e[i].v);
    49             }
    50         }
    51     }
    52     return dep[t];
    53 }
    54 ll dfs(ll nd, ll val) {
    55     if(val == 0) return 0;
    56     if(nd == t) return val;
    57     for(int i = head[nd] ; ~i ; i = e[i].nxt) {
    58         ll u = e[i].v;
    59         if(dep[u] == dep[nd] + 1 and e[i].c) {
    60             ll k = dfs(u , min(e[i].c,val));
    61             if(k) {
    62                 e[i].c -= k;
    63                 e[i ^ 1].c += k;
    64                 return k;
    65             }
    66 
    67         }
    68     }
    69     return 0;
    70 }
    71 int main() {
    72     n = read();
    73     m = read();
    74     s = read();
    75     t = read();
    76     memset(head,-1,sizeof(head));
    77     for(int i=1; i<=m; i++) {
    78         ll t1,t2,t3;
    79         t1 = read();
    80         t2 = read();
    81         t3 = read();
    82         add(t1,t2,t3);
    83         add(t2,t1,0);
    84     }
    85     while(bfs()) {
    86         while(1) {
    87             int d = dfs(s,inf);
    88             if(d == 0) break;
    89             else ans += d;
    90         }
    91     }
    92     printf("%lld
    ",ans);
    93     return 0;
    94 }
  • 相关阅读:
    动态设置字体大小需要注意的点
    getDimension与getDimensionPixelOffset与getDimensionPixelSize的区别
    统计图钻取的明细报表在非模态窗口中显示
    局部区块多个报表 TAB 页切换及局部区块的参数查询
    分栏报表制作攻略
    多值关联过滤
    鼠标悬停出现提示信息怎么做
    复杂报表设计之动态报表
    Logo(图片)作为报表水印的解决方法
    分组填报表的制作
  • 原文地址:https://www.cnblogs.com/qmcp/p/9686202.html
Copyright © 2011-2022 走看看