zoukankan      html  css  js  c++  java
  • SGU 176 Flow construction(有源汇上下界最小流)

    Description

    176. Flow construction

    time limit per test: 1 sec. 
    memory limit per test: 4096 KB
    input: standard 
    output: standard



    You have given the net consisting of nodes and pipes; pipes connect the nodes. Some substance can flow by pipes, and flow speed in any pipe doesn't exceed capacity of this pipe. 
    The substance cannot be accumulated in the nodes. But it is being produced in the first node with the non-negative speed and being consumed with the same speed in the last node. 
    You have some subset taken from the set of pipes of this net. You need to start the motion of substance in the net, and your motion must fully fill the pipes of the given subset. Speed of the producing substance in the first node must be minimal. 
    Calculate this speed and show the scene of substance motion. 
    Remember that substance can't be accumulated in the nodes of the net.

    Input
    Two positive integer numbers N (1<=N<=100) and M have been written in the first line of the input - numbers of nodes and pipes. 
    There are M lines follows: each line contains four integer numbers Ui, Vi, Zi, Ci; the numbers are separated by a space. Ui is the beginning of i-th pipe, Vi is its end, Zi is a capacity of i-th pipe (1<=Zi<=10^5) and Ci is 1 if i-th pipe must be fully filled, and 0 otherwise. 
    Any pair of nodes can be connected only by one pipe. If there is a pipe from node A to node B, then there is no pipe from B to A. Not a single node is connected with itself. 
    There is no pipe which connects nodes number 1 and N. Substance can flow only from the beginning of a pipe to its end.

    Output
    Write one integer number in the first line of the output - it ought to be the minimal speed of the producing substance in the first node. 
    Write M integers in the second line - i-th number ought to be the flow speed in the i-th pipe (numbering of pipes is equal to the input). 
    If it is impossible to fill the given subset, write "Impossible".
     
    题目大意:有n个点,m条有向边,每条有向边有一个容量的上界,有一些变的流量必须为满(即下界=上界),求从1到n的最小流。
    思路:像无源无汇上下界可行流那样先建好图,记图的超级源点为SS,超级汇点为TT。先从SS到TT跑一遍最大流,然后加边n→1(即T→S)容量为无穷大,然后再从SS到TT跑一遍最大流,若与SS关联的边满容量,则有解,其中最小流为最后加进去的n→1的边的流量,找边的流量跟无源无汇上下界可行流一样,否则无解。
    小证明:第一次跑最大流消掉了环,之后加上T→S的边,我估计是所有的边都只能走T→S这条边来增广了……
    PS:之前稍微写错了一点,一直PE,估计它要我输出数字我输出了impossible(这样居然不是WA啊可恶)。
     
      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <queue>
      5 using namespace std;
      6 
      7 const int MAXN = 110;
      8 const int MAXE = MAXN * MAXN * 2;
      9 const int INF = 0x3fff3fff;
     10 
     11 struct SAP {
     12     int head[MAXN], gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN];
     13     int to[MAXE], next[MAXE], flow[MAXE], cap[MAXE];
     14     int n, ecnt, st, ed;
     15 
     16     void init() {
     17         memset(head, 0, sizeof(head));
     18         ecnt = 2;
     19     }
     20 
     21     void add_edge(int u, int v, int c) {
     22         to[ecnt] = v; cap[ecnt] = c; flow[ecnt] = 0; next[ecnt] = head[u]; head[u] = ecnt++;
     23         to[ecnt] = u; cap[ecnt] = 0; flow[ecnt] = 0; next[ecnt] = head[v]; head[v] = ecnt++;
     24         //printf("%d->%d %d
    ", u, v, c);
     25     }
     26 
     27     void bfs() {
     28         memset(dis, 0x3f, sizeof(dis));
     29         queue<int> que; que.push(ed);
     30         dis[ed] = 0;
     31         while(!que.empty()) {
     32             int u = que.front(); que.pop();
     33             ++gap[dis[u]];
     34             for(int p = head[u]; p; p = next[p]) {
     35                 int &v = to[p];
     36                 if(cap[p ^ 1] > flow[p ^ 1] && dis[v] > n) {
     37                     dis[v] = dis[u] + 1;
     38                     que.push(v);
     39                 }
     40             }
     41         }
     42     }
     43 
     44     int Max_flow(int ss, int tt, int nn) {
     45         st = ss, ed = tt, n = nn;
     46         int ans = 0, minFlow = INF, u;
     47         for(int i = 0; i <= n; ++i) {
     48             cur[i] = head[i];
     49             gap[i] = 0;
     50         }
     51         u = pre[st] = st;
     52         bfs();
     53         while(dis[st] < n) {
     54             bool flag = false;
     55             for(int &p = cur[u]; p; p = next[p]) {
     56                 int &v = to[p];
     57                 if(cap[p] > flow[p] && dis[u] == dis[v] + 1) {
     58                     flag = true;
     59                     minFlow = min(minFlow, cap[p] - flow[p]);
     60                     pre[v] = u;
     61                     u = v;
     62                     if(u == ed) {
     63                         ans += minFlow;
     64                         while(u != st) {
     65                             u = pre[u];
     66                             flow[cur[u]] += minFlow;
     67                             flow[cur[u] ^ 1] -= minFlow;
     68                         }
     69                         minFlow = INF;
     70                     }
     71                     break;
     72                 }
     73             }
     74             if(flag) continue;
     75             int minDis = n - 1;
     76             for(int p = head[u]; p; p = next[p]) {
     77                 int &v = to[p];
     78                 if(cap[p] > flow[p] && dis[v] < minDis) {
     79                     minDis = dis[v];
     80                     cur[u] = p;
     81                 }
     82             }
     83             if(--gap[dis[u]] == 0) break;
     84             ++gap[dis[u] = minDis + 1];
     85             u = pre[u];
     86         }
     87         return ans;
     88     }
     89 } G;
     90 
     91 int n, m, a, b, c, d, x;
     92 int f[MAXN], down[MAXE], id[MAXE];
     93 
     94 int main() {
     95     scanf("%d%d", &n, &m);
     96     G.init();
     97     int sum = 0;
     98     int ss = n + 1, tt = n + 2;
     99     for(int i = 1; i <= m; ++i) {
    100         scanf("%d%d%d%d", &a, &b, &c, &d);
    101         if(d == 1) {
    102             f[a] -= c;
    103             f[b] += c;
    104             down[i] = c;
    105             //G.add_edge(a, b, 0);
    106         }
    107         else {
    108             id[i] = G.ecnt;
    109             G.add_edge(a, b, c);
    110         }
    111     }
    112     for(int i = 1; i <= n; ++i) {
    113         if(f[i] > 0) sum += f[i], G.add_edge(ss, i, f[i]);
    114         else G.add_edge(i, tt, -f[i]);
    115     }
    116     sum -= G.Max_flow(ss, tt, tt);
    117     int ans_id = G.ecnt;
    118     G.add_edge(n, 1, INF);
    119     if(sum == G.Max_flow(ss, tt, tt)) {
    120         printf("%d
    ", G.flow[ans_id]);
    121         for(int i = 1; i < m; ++i) {
    122             if(down[i]) printf("%d ", down[i]);
    123             else printf("%d ", G.flow[id[i]]);
    124         }
    125         if(down[m]) printf("%d
    ", down[m]);
    126         else printf("%d
    ", G.flow[id[m]]);
    127     }
    128     else printf("Impossible
    ");
    129     return 0;
    130 }
    View Code

    顺便放上zhuzeyuan 2006年国家集训队作业对此题的解题报告……

    ——————————————————————————————————————————————————————————————————————————————————————————————

    问题名称:Flow Construction

    问题来源:SGU

    解决程度:完美解决

     

    问题简述:

    N个节点的网络,有一个起点S,和一个终点T,另外有M根有向的管子连接它们。每根管子有单位时间的流量限制,并且其中有些管子必须完全充满(实际流量等于流量限制)。起点处可以制造物质,终点处可以吸收物质,求最小的制造速度(吸收速度),使得流量满足条件。

     

    分析:

    仔细分析问题,其实是“有上下界的最小流”。“流量限制”是网络流中的“容量上届”,必须要充满的管道,容量下界等于上界。

    翻开了各类书籍,可以找到用附加网求网络最小流的算法。

     

    经典算法:

    建立附加源s',和附加汇t'。对每个顶点a,添加一条新弧at',容量设为所有以a为尾的弧的下限之和;对每个顶点a,添加一条新弧s'a,容量设为所有以a为头的弧的下限之和。原图中的边保留,容量设定为上限减去下限。添加一条新弧t->s(顶点n->1),容量无限大。(注,这里没有必要再设置s->t,书上是错的)

    s't'的最大流,如果能让所有s'出去的弧都满载,就有解。否则无解。如果有解,再利用t->s求最大流,将流量缩小。

     

    算法质疑:

    在附加网,以及流量缩小的地方,该算法容易被引起质疑:

    例如如下情况,B->C容量为1,要求被满载。

    S-->--A--->--B--->---T

                    /

               /  /

                C/

    如用上述方法,将得到最小流量为0,也就是仅仅出现A->B->C->A一个环的情况,而不是S->A->B->C->A->B->T的流量为1的结果。

    但是要注意网络流的定义,其并没有说这种情况不能发生。对于本题,设定S制造速度为0,一开始ABC中间就有物质在不停地运动(题目中没有说不可以)。

    流量缩小的过程并不尽如人意,比如:

    S--->--T

            /

       /  /

        A/

    T->A容量为1要求满载。

    那么在缩小流量的时候,会把S->T->A->S圈上的S->T删掉,得到流量-1……为什么呢?

    因为在网络流的定义中,规定了不能有弧指向S,也不能有弧流出T。我们必须要再设定超级源和超级汇……有没有简便的方法呢?

     

    算法改进:

    不缩小流?很容易地,我们找到了许多反例。对前面“标准算法”的思想理解透彻,仔细思考后发现:

    t->s(也就是N->1)弧的流量,就是s点的制造速度。因此,只要在t->s的弧上设定费用为1,对附加网求最小费用最大流就可以了。

     

    算法最终改进:

    事实上,进行两次最大流即可。

    第一次不添加t->s的弧,求最大流。第二次把这条弧填进去,再尝试把s’流出的弧都满载,进行一次最大流。不难证明正确性。

    我觉得我的方法适用于所有“上下界的最小流”问题,简单易行,时间复杂度平均意义上更低。

     

    期望得分:满分

    ——————————————————————————————————————————————————————————————————————————————————————————————

  • 相关阅读:
    python中元类(metaclass)的理解
    aiohttp
    async/await
    asyncio
    协程
    Bayesian Non-Exhaustive Classification A case study:online name disambiguation using temporal record streams
    技术网址
    网站
    各种网址
    OpenGL学习网址2
  • 原文地址:https://www.cnblogs.com/oyking/p/3253353.html
Copyright © 2011-2022 走看看