zoukankan      html  css  js  c++  java
  • SGU176 Flow construction 有上下界的最小流

    这里参看了大牛的解题思路,学习了很多。原来上下界流的求法是这么的灵活,尤其我是用的临界表存储的边,删除更新很不方便。

    http://www.shuizilong.com/house/archives/sgu-176-flow-construction/

    http://hi.baidu.com/lxc_0601/blog/item/39e4e2ecd3be0b2f62d09f95.html

    这里要进行说明的求解一个有上下界的网络流的步骤:

    1.首先进行构图,对于那么对流量没有限制的边,我们直接将容量赋值为原始的容量,而对于有流量要求的边,我们将容量减去下界并将其等价与无下界的边。最后就是添加一个附加汇点和一个附加源点,从附加源点连向每个顶点的容量为以该点所有流入的下界流量总和,每个顶点流向附加汇点是该点流出的下界流量总和。

    2.我们要添加一条从汇点到源点流量为INF的边,这条边的意义在于,能够使得源点会汇点满足成为流量平衡条件的普通节点。

    3.我们在以附加源点和附加汇点求一次最大流,如果所有的到附加汇点的边都满载,那么说明这个网络是存在满足所有下界的可行流的。因为去除了下界容量的图具备这个能力。但是此时的可行流(从汇点流向源点的流量)并不一定是最小流,因为满足情况的可行流是不唯一的。

    4.紧接着,我们在原图上从汇点向源点求一次最大流(此时要删除掉那条从汇点到源点的INF的边),此时便是一个缩流的过程,旨在试探图中是否还存在流量去替代汇点到源点的流量。这里计算出来的结果可能比我们已得到的可行流还要大,意思是说从汇点到源点有的是空间,因此也就不必连接那条INF的边了,整个网络的流量可以为0,网络中存在环流。

    由于这里免不了会进行删边的操作,因此我们直接找到那条边,把流量赋值为0就可以了。

    代码如下:31MS

    #include <cstring>
    #include <cstdio>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #define INF 0x3fffffff
    #define RE(x) ((x)^1)
    using namespace std;
    
    int N, M, head[150], dis[150], idx, sum;
    
    const int vsource = 110, vsink = 111;
    int source, sink; 
     
    queue<int>q;
    
    struct Point
    {
        int in, out; 
    }p[105];
    
    struct Edge
    {
        int v, cap, next, rec;
    }e[20000];
    
    struct 
    {
        int x, y, b, c;
    }info[10015];
    
    void insert(int f, int t, int c, int rec)
    {
        ++idx; 
        e[idx].v = t, e[idx].cap = c;
        e[idx].rec = rec;
        e[idx].next = head[f], head[f] = idx;
    }
    
    void init()
    {
        idx = -1;
        memset(head, 0xff, sizeof (head));
    }
    
    bool spfa(int u, int sk)
    {
        memset(dis, 0xff, sizeof (dis));
        dis[u] = 0;
        q.push(u);
        while (!q.empty()) {
            u = q.front();
            q.pop();
            for (int i = head[u]; i != -1; i = e[i].next) {
                if (dis[e[i].v] == -1 && e[i].cap > 0) {
                    dis[e[i].v] = dis[u] + 1;
                    q.push(e[i].v);
                }
            }
        }
        return dis[sk] != -1;
    }
    
    int dfs(int u, int sk, int flow)
    {
        if (u == sk) {
            return flow;
        }
        int tf = 0, sf;
        for (int i = head[u]; i != -1; i = e[i].next)
        {
            if (dis[u]+1 == dis[e[i].v] && e[i].cap > 0 && (sf = dfs(e[i].v, sk, min( flow-tf, e[i].cap )))) {
                e[i].cap -= sf; 
                e[RE(i)].cap += sf;
                tf += sf;
                if (tf == flow) {
                    return tf;
                }
            }
        }
        if (!tf) {
            dis[u] = -1;
        }
        return tf;
    }
    
    int Dinic(int u, int sk, int flow)
    {
        int ans = 0; 
        while (spfa(u, sk)) {
            ans += dfs(u, sk, flow);
        }
        return ans;
    }
    
    /*
    5 6
    1 2 1 0
    2 5 1 0
    1 4 2 1
    4 5 4 0
    1 3 3 0
    3 4 2 1
    
    4
    0 0 2 4 2 2
    
    5 5 
    1 2 1 0
    2 3 2 1
    3 4 2 1
    4 2 2 1
    3 5 1 0
    
    0
    0 2 2 2 0
    */
    int main()
    { 
        int ans1, ans2, ans;
        init();
        scanf("%d %d", &N, &M);
        if (N == 0) {
            puts("Impossible");
            return 0;
        }
        source = 1, sink = N;
        for (int i = 1; i <= M; ++i) {
            scanf("%d %d %d %d", &info[i].x, &info[i].y, &info[i].b, &info[i].c);
            if (info[i].c) {
                sum += info[i].b;
                p[info[i].x].out += info[i].b;
                p[info[i].y].in += info[i].b;
                insert(info[i].x, info[i].y, 0, 0);
                insert(info[i].y, info[i].x, 0, 0);
            }
            else {
                insert(info[i].x, info[i].y, info[i].b, info[i].b);
                insert(info[i].y, info[i].x, 0, 0);
            }
        }
        // 接下来遍历所有节点,添加附加源点
        for (int i = 1; i <= N; ++i) {
            insert(vsource, i, p[i].in, p[i].in);
            insert(i, vsource, 0, 0);
            insert(i, vsink, p[i].out, p[i].out);
            insert(vsink, i, 0, 0);
        }
        insert(sink, source, INF, INF);
        insert(source, sink, 0, 0);
        
        if (Dinic(vsource, vsink, sum) != sum) {
            puts("Impossible");
            return 0;
        }
        
        ans1 = e[RE(head[sink])].cap;
        // 这里得到的是一个原网络的可行流,并非最小或者最大流
        
        e[head[sink]].cap = 0;
        e[RE(head[sink])].cap = 0;
        
        ans2 = Dinic(sink, source, INF);
        // 寻找缩流
        ans = ans1 - ans2;
        if (ans < 0) {  // 如果愿网络在不加汇点到源点的INF边足以提供多余可行流的流量
            // 那么最小流就可以等于零,内部成环
            e[head[sink]].cap = 0;
            e[RE(head[sink])].cap = 0;
            insert(vsource, source, -ans, -ans);
            Dinic(vsource, sink, INF);
            ans = 0;
        }
        printf("%d\n", ans);
        for (int i = 1; i <= M; ++i) {
            printf(i == 1 ? "%d" : " %d", e[(i-1)*2+1].cap+info[i].b*info[i].c);
        }
        puts("");
        return 0;
    }

     二分法枚举法:140MS

    #include <cstring>
    #include <cstdio>
    #include <cstdio>
    #include <algorithm>
    #define INF 0x3fffffff
    #define RE(x) ((x)^1)
    using namespace std;
    
    int N, M, head[150], dis[150], idx, sum;
    
    const int vsource = 110, vsink = 111;
    int source, sink, que[150], front; 
    
    struct Point
    {
        int in, out; 
    }p[105];
    
    struct Edge
    {
        int v, cap, next, rec;
    }e[20000];
    
    struct 
    {
        int x, y, b, c;
    }info[10015];
    
    void insert(int f, int t, int c, int rec)
    {
        ++idx; 
        e[idx].v = t, e[idx].cap = c;
        e[idx].rec = rec;
        e[idx].next = head[f], head[f] = idx;
    }
    
    void init()
    {
        idx = -1;
        memset(head, 0xff, sizeof (head));
    }
    
    bool spfa(int u, int sk)
    {
        memset(dis, 0xff, sizeof (dis));
        dis[u] = 0;
        front = 0;
        que[front++] = u;
        while (front) {
            u = que[--front];
            for (int i = head[u]; i != -1; i = e[i].next) {
                if (dis[e[i].v] == -1 && e[i].cap > 0) {
                    dis[e[i].v] = dis[u] + 1;
                    que[front++] = e[i].v;
                }
            }
        }
        return dis[sk] != -1;
    }
    
    int dfs(int u, int sk, int flow)
    {
        if (u == sk) {
            return flow;
        }
        int tf = 0, sf;
        for (int i = head[u]; i != -1; i = e[i].next)
        {
            if (dis[u]+1 == dis[e[i].v] && e[i].cap > 0 && (sf = dfs(e[i].v, sk, min( flow-tf, e[i].cap )))) {
                e[i].cap -= sf, e[RE(i)].cap += sf;
                tf += sf;
                if (tf == flow) {
                    return tf;
                }
            }
        }
        if (!tf) {
            dis[u] = -1;
        }
        return tf;
    }
    
    int Dinic(int u, int sk, int flow)
    {
        int ans = 0; 
        while (spfa(u, sk)) {
            ans += dfs(u, sk, flow);
        }
        return ans;
    }
    
    int bsearch(int l, int r)
    {
        int mid, ans = -1;
        while (l <= r) {
        //    printf("mid = %d\n", mid);
        //    printf("l=%d, r=%d\n", l, r);
            mid = (l+r) >> 1;
            e[head[sink]].cap = mid;
            e[RE(head[sink])].cap = 0;
            if (Dinic(vsource, vsink, INF) == sum) {
                r = mid-1;
                ans = mid;
            }
            else {
                l = mid+1;
            }
            for (int i = 0; i <= idx; ++i) {
                e[i].cap = e[i].rec;
            }
        }
        if (ans != -1) {
            e[head[sink]].cap = ans;
            e[RE(head[sink])].cap = 0;
            Dinic(vsource, vsink, INF);
        }
        return ans;
    }
    /*
    5 6
    1 2 1 0
    2 5 1 0
    1 4 2 1
    4 5 4 0
    1 3 3 0
    3 4 2 1
    
    5 5 
    1 2 1 0
    2 3 2 1
    3 4 2 1
    4 2 2 1
    3 5 1 0
    
    */
    int main()
    { 
        int ans1, ans2;
        init();
        scanf("%d %d", &N, &M);
        if (N == 0) {
            puts("Impossible");
            return 0;
        }
        source = 1, sink = N;
        for (int i = 1; i <= M; ++i) {
            scanf("%d %d %d %d", &info[i].x, &info[i].y, &info[i].b, &info[i].c);
            if (info[i].c) {
                p[info[i].x].out += info[i].b;
                p[info[i].y].in += info[i].b;
                insert(info[i].x, info[i].y, 0, 0);
                insert(info[i].y, info[i].x, 0, 0);
            }
            else {
                insert(info[i].x, info[i].y, info[i].b, info[i].b);
                insert(info[i].y, info[i].x, 0, 0);
            }
        }
        // 接下来遍历所有节点,添加附加源点
        for (int i = 1; i <= N; ++i) {
            sum += p[i].in;
            insert(vsource, i, p[i].in, p[i].in);
            insert(i, vsource, 0, 0);
            insert(i, vsink, p[i].out, p[i].out);
            insert(vsink, i, 0, 0);
        }
        insert(sink, source, INF, INF);
        insert(source, sink, 0, 0);
        ans1 = bsearch(0, 10000000);
        if (ans1 == -1) {
            puts("Impossible");
            return 0;
        }
        printf("%d\n", ans1);
        for (int i = 1; i <= M; ++i) {
            printf(i == 1 ? "%d" : " %d", e[(i-1)*2+1].cap+info[i].b*info[i].c);
        }
        return 0;
    }
  • 相关阅读:
    【考试反思】联赛模拟测试16
    【考试反思】联赛模拟测试15
    【考试反思】联赛模拟测试14
    【考试反思】联赛模拟测试13
    【学习笔记】震惊,全机房都会分块了,就我没有
    挂分宝典
    「计数」客星璀璨之夜 + 大佬
    第五阶段反思
    「板子」线段树维护单调栈
    阶段反思
  • 原文地址:https://www.cnblogs.com/Lyush/p/2581498.html
Copyright © 2011-2022 走看看