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\n", 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;
}

时间: 2024-10-14 21:08:43

SGU 176.Flow construction的相关文章

sgu 176 Flow construction(有源汇的上下界最小流)

[题目链接] http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=11025 [模型] 有源汇点的上下界最小流.即既满足上下界又满足流量平衡的最小流量. [思路] 按照可行流构造网络.不连t->s的边先跑一遍附加源汇点的最大流,然后连t->s一条inf边,在残量网络上跑一遍最大流.第一次求最大流所以能走的边都已经流满,第二次求附加源汇点最大流t->s的流量就会尽可能小. 另外还可以二分下界mid,然后连边(T,S,mid

【SGU 176】 Flow construction

176. Flow construction time limit per test: 0.5 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 an

Flow construction SGU - 176 有源汇有上下界最小流 二分法和回流法

/** 题目:Flow construction SGU - 176 链接:https://vjudge.net/problem/SGU-176 题意: 有源汇有上下界的最小流. 给定n个点,m个管道.每个管道给出u,v,z,c.u表示起点,v表示终点,z表示容量,如果c==1,那么表示还有下界为z. 如果c==0,表示没有下界. 求从1到n的最小流. 思路: 第一种做法: 转化为无源汇求超级源S到超级汇T的最大流flow1(此时从s出发的流和为flow1),然后讲t到s的边删掉(可以使流量等于

sgu Flow construction

Flow construction 题目: 给出N个节点M条水管,要求在满足上下界的情况下.满足起点最小的流量. 算法: 这是最小流????不知道.只知道用求解上下界最大流的方法就过了. 做这题收获了很多东西.知道了同一点的flow是真实的流量值,虽然以前在书上或论文中看到过,不过印象不深,但是经过这题深刻的懂了.就是这题输出的时候有点麻烦...要记录每次的路径,然后用我刚才说的那个,同一个点的flow是真实的流量值!!!! #include <iostream> #include <a

sgu 176 上下界网络流最小可行流带输出方案

算法步骤: 1. 先将原图像最大可行流那样变换,唯一不同的是不加dst->src那条边来将它变成无源无汇的网络流图.直接跑一边超级源到超级汇的最大流. 2. 加上刚才没有加上的那条边p 3. 再跑一遍超级源汇之间的最大流,p的流量就是我们要求的最小可行流流量(等于其反向边的"容量") 收获: 1. 最大可行流和最小可行流,当我们把其残量网络求出来后,其流量就是dst->src的残量. 每条边在此时的流量 = 流量下界 + 转换后对应边的流量 1 #include <c

sgu176 Flow Construction【有源汇有上下界最小流】

同样是模板题. 首先将有源汇转换为无源汇,假设原来的源汇为st,我们加入的源汇为ST,那么我们应该从t到s连一条流量为+∞的边,使原来的st满足收支平衡,退化为普通节点. 分离必要边和其他边,从S到T跑最大流,所有与源或者汇相连的边都流满则证明有解. 去掉t到s容量为+∞的边,去掉必要边,从t到s跑最大流. 把得到的答案相减即可. 如果我们得到的答案是负的,那么说明它内部t到s连成了环,那么我们加上S到s容量为-ans的边,跑S到t的最大流,这样所有的边的流量应该就是0,再加上流量下界即为答案.

网络流专栏

最大流 POJ 1273 Drainage Ditches POJ 1274 The Perfect Stall (二分图匹配) POJ 1698 Alice's Chance(构图) POJ 1459 Power Network(构图) POJ 2112 Optimal Milking (二分) POJ 2455 Secret Milking Machine (二分) POJ 3189 Steady Cow Assignment (枚举) POJ 1637 Sightseeing tour (

Soj题目分类

-----------------------------最优化问题------------------------------------- ----------------------常规动态规划  SOJ1162 I-Keyboard  SOJ1685 Chopsticks SOJ1679 Gangsters SOJ2096 Maximum Submatrix  SOJ2111 littleken bg SOJ2142 Cow Exhibition  SOJ2505 The County

网络流柱

最大流量 POJ 1273 Drainage Ditches POJ 1274 The Perfect Stall (二分图匹配) POJ 1698 Alice's Chance(构图) POJ 1459 Power Network(构图) POJ 2112 Optimal Milking (二分) POJ 2455 Secret Milking Machine (二分) POJ 3189 Steady Cow Assignment (枚举) POJ 1637 Sightseeing tour