zoukankan      html  css  js  c++  java
  • SGU 176.Flow construction (有上下界的最大流)

    时间限制:0.5s

    空间限制:4M

    题意:

      有一个由管道组成的网络,有n个节点(n不大于100),1号节点可以制造原料,最后汇集到n号节点。原料通过管道运输。其中有一些节点有管道连接,这些管道都有着最大的流量限制,其中有一些管道必须充满。求1号节点最小的制造原料速度。如果原料不能运输到n,输出“Impossible”


    Solution

              第一道有上下界的网络流。从理解不太深刻,wa了很多次后。完全理解了有上下界网络流的算法。

              首先,要构造一个伴随网络,由此判断,是否能让所有的下界满足,并连通源汇点。

              由于是求最小流,我们需要知道,是否能从汇点找到一条到源点的增广路。使得最大流减小。

              这时可能求出负流flow,只要新加一个节点0,添加到汇点的一条容量为-flow的边,即可让负流变为0;

              具体的构造方法在程序注释里。

    /*
          有容量上下界的最大流算法
             1)cap(u,v)为u到v的边的容量
             2)Gup(u,v)为u到v的边流量上界
             3)Glow(u,v)为u到v的边流量下界
             4)st(u)代表点u的所有出边的下界之和
             5)ed(u)代表点u的所有入边的下界之和
             6)S为源点,T为汇点
           新网络D的构造方法:
             1)加入虚拟源点SS,ST
             2)如果边(u,v)的容量cap(u,v)=Gup(u,v)-Glow(u,v)
             3)对于每个点v,加入边(SS,v)=ed(v);
             4)对于每个点u,加入边(u,ST)=st(u);
             5)cap(T,S)=+∞;
             6)tflow为所有边的下界之和
           求SS到ST的最大流,若最大流不等于tflow,则不存在可行流,此问题无解。
           在新网络D中去掉所有与SS,ST相连的边。求最大流。
           最后将两个流值相加
           最小流,第二次最大流从T到S运行。
    
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define ms(a,b) memset(a,b,sizeof a)
    using namespace std;
    const int INF = 111;
    struct node {
        int u, v, c, next;
    } edge[INF * INF << 2];
    int Gup[INF][INF], Glow[INF][INF], st[INF], ed[INF], cap[INF][INF], tflow;
    int  pHead[INF*INF], SS, ST, S, T, nCnt, ans;
    //同时添加弧和反向边, 反向边初始容量为0
    void addEdge (int u, int v, int c) {
        edge[++nCnt].v = v, edge[nCnt].u = u, edge[nCnt].c = c;
        edge[nCnt].next = pHead[u]; pHead[u] = nCnt;
        edge[++nCnt].v = u, edge[nCnt].u = v, edge[nCnt].c = 0;
        edge[nCnt].next = pHead[v]; pHead[v] = nCnt;
    }
    int SAP (int pStart, int pEnd, int N) {
        int numh[INF], h[INF], curEdge[INF], pre[INF];
        int cur_flow, flow_ans = 0, u, neck, i, tmp;
        ms (h, 0); ms (numh, 0); ms (pre, -1);
        for (i = 0; i <= N; i++) curEdge[i] = pHead[i];
        numh[0] = N;
        u = pStart;
        while (h[pStart] <= N) {
            if (u == pEnd) {
                cur_flow = 1e9;
                for (i = pStart; i != pEnd; i = edge[curEdge[i]].v)
                    if (cur_flow > edge[curEdge[i]].c) neck = i, cur_flow = edge[curEdge[i]].c;
                for (i = pStart; i != pEnd; i = edge[curEdge[i]].v) {
                    tmp = curEdge[i];
                    edge[tmp].c -= cur_flow, edge[tmp ^ 1].c += cur_flow;
                }
                flow_ans += cur_flow;
                u = neck;
            }
            for ( i = curEdge[u]; i != 0; i = edge[i].next) {
                if (edge[i].v > N) continue; //重要!!!
                if (edge[i].c && h[u] == h[edge[i].v] + 1)     break;
            }
            if (i != 0) {
                curEdge[u] = i, pre[edge[i].v] = u;
                u = edge[i].v;
            }
            else {
                if (0 == --numh[h[u]]) continue;
                curEdge[u] = pHead[u];
                for (tmp = N, i = pHead[u]; i != 0; i = edge[i].next) {
                    if (edge[i].v > N) continue; //重要!!!
                    if (edge[i].c)  tmp = min (tmp, h[edge[i].v]);
                }
                h[u] = tmp + 1;
                ++numh[h[u]];
                if (u != pStart) u = pre[u];
            }
        }
        return flow_ans;
    }
    int solve (int n) {
        //建立伴随网络
        SS = n + 1, ST = n + 2;
        for (int i = 1; i <= n; i++) {
            if (ed[i]) addEdge (SS, i, ed[i]);
            if (st[i]) addEdge (i, ST, st[i]);
        }
        //T到S添加一条无限容量边
        addEdge (T, S, 0x7ffffff);
        //判断可行流
        int tem = SAP (SS, ST, ST);
        if (tem != tflow) return -1;
        else {
            edge[nCnt].c = edge[nCnt - 1].c = 0; //删除S到T的无限容量边
            int kkk = SAP (T, S, T);
            return 1;
        }
    }
    int n, m, x, y, c, sta;
    int main() {
        /*
               建图,前向星存边,表头在pHead[],边计数 nCnt.
               S,T分别为源点和汇点
        */
        scanf ("%d %d", &n, &m);
        nCnt = 1;
        for (int i = 1; i <= m; i++) {
            scanf ("%d %d %d %d", &x, &y, &c, &sta);
            Gup[x][y] = c;
            if (sta) {
                Glow[x][y] = c;
                st[x] += c, ed[y] += c;
                tflow += c;
            }
            addEdge (x, y, Gup[x][y] - Glow[x][y]);
        }
        S = 1, T = n;
        ans = 0;
        if (solve (n) > 0) {
            for (int i = 2; i <= nCnt; i += 2) {
                if (edge[i].v <= T && edge[i].u == 1)
                    ans += Gup[edge[i].u][edge[i].v] - edge[i].c;
                if (edge[i].u <= T && edge[i].v == 1)
                    ans -= Gup[edge[i].u][edge[i].v] - edge[i].c;
            }
            if (ans < 0) {
                S = 0;
                addEdge (S, 1, -ans);
                ans = 0;
                SAP (S, T, T);
            }
            printf ("%d
    ", ans);
            for (int i = 2; i <= 2 * m; i += 2)
                printf ("%d ", Gup[edge[i].u][edge[i].v] - edge[i].c);
        }
        else puts ("Impossible");
        return 0;
    }
    View Code
  • 相关阅读:
    Java并发之CAS与AQS简介
    关系型数据库三范式
    分库分表使用场景及设计方式
    项目部署到tomcat出错(tomcat运行时的JDK版本)
    手写一个简化版Tomcat
    java并发之并发工具
    java并发之停止线程
    class中static总结-静态成员函数和静态成员变量
    45 孩子们的游戏(圆圈中最后剩下的数) + list操作总结+ for_each多记忆容易忘记
    C++ split分割字符串函数
  • 原文地址:https://www.cnblogs.com/keam37/p/3980136.html
Copyright © 2011-2022 走看看