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的边删掉(可以使流量等于容量,这样求t到s的最大流就不会经过他了。)
求t到s的最大流flow2(从s出发的流减少的量).是为了回流,因为原先求flow1的过程,是为了满足下界的可行流。这个在原图的可行流可能可以变得更小,通过回流使其缩小。
求t到s的最大流并不会影响原来附加边的流量。所以保证了是下界满足的可行流。
然后用flow1-flow2就是结果。

第二种做法:
构造无源汇有上下界的可行流做法,只不过t到s的边的上下界要改一下。
二分t到s的上界a,下界为0,如果是可行流最小的a便是最小流。如果是求最大流,那么就是二分t到s的下界a,上界无穷,如果是可行流,那么最大的a便是最大流。
不懂的看文档解释。https://wenku.baidu.com/view/0f3b691c59eef8c75fbfb35c.html

关于每次二分,处理一次最大流,那么下次在计算最大流的时候,难道又要重新建图?
反正是结构体存的,新加一个变量存储。下次直接从这个变量获取即可。注意t到s这条边的修改。或者最后面再加进去。

*/

第一种做法

#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int N = 210;///n+m=1365
int in[N];
int out[N];
struct Edge{
    int from, to, cap, flow;
    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct Dinic{
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[N];
    bool vis[N];
    int d[N];
    int cur[N];

    void init(int n)
    {
        this->n = n;
        for(int i = 0; i <= n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int cap)
    {
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BFS()
    {
        memset(vis, 0, sizeof vis);
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!Q.empty())
        {
            int x = Q.front();
            Q.pop();
            for(int i = 0; i < G[x].size(); i++)
            {
                Edge &e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)
                {
                    vis[e.to] = 1;
                    d[e.to] = d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }

    int DFS(int x,int a)
    {
        if(x==t||a==0) return a;
        int flow = 0, f;
        for(int &i = cur[x]; i < G[x].size(); i++)
        {
            Edge& e = edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow += f;
                edges[G[x][i]^1].flow -= f;
                flow += f;
                a -= f;
                if(a==0) break;
            }
        }
        return flow;
    }

    int Maxflow(int s,int t)
    {
        this->s = s, this->t = t;
        int flow = 0;
        while(BFS())
        {
            memset(cur, 0, sizeof cur);
            flow += DFS(s,INF);
        }
        return flow;
    }
};
int dw[N*N];
int main()
{
    int n, m;
    while(scanf("%d%d",&n,&m)==2)
    {
        Dinic dinic;
        dinic.init(n+1);
        int u, v, cap, flag;///1,n为普通源汇。
        int s = 0, t = n+1;///超级源汇。
        memset(dw, 0, sizeof dw);
        memset(in, 0, sizeof in);
        memset(out, 0, sizeof out);
        for(int i = 0; i<m; i++){
            scanf("%d%d%d%d",&u,&v,&cap,&flag);
            if(flag==1){
                dw[i] = cap;
                dinic.AddEdge(u,v,0);
                out[u]+=cap;
                in[v]+=cap;
            }else
            {
                dinic.AddEdge(u,v,cap);

            }
        }
        int ts;
        dinic.AddEdge(n,1,INF);
        ts = dinic.edges.size()-2;
        int sum = 0;
        for(int i = 1; i <= n; i++){
            if(in[i]>out[i]){
                dinic.AddEdge(s,i,in[i]-out[i]);
                sum += in[i]-out[i];
            }
            if(in[i]<out[i]){
                dinic.AddEdge(i,t,out[i]-in[i]);
            }
        }
        int flow = dinic.Maxflow(s,t);
        if(flow!=sum){
            printf("Impossible\n"); continue;
        }
        dinic.edges[ts].cap = dinic.edges[ts].flow;///使其求n到1的最大流无法经过。
        int flow2 = dinic.Maxflow(n,1);///回流
        printf("%d\n",flow-flow2);
        for(int i = 0; i < m; i++){
            if(i==m-1){
                printf("%d\n",dinic.edges[2*i].flow+dw[i]);
            }else
            {
                printf("%d ",dinic.edges[2*i].flow+dw[i]);
            }
        }
    }
    return 0;
}

第二种做法

#include<iostream>
#include<cstring>
#include<vector>
#include<map>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int N = 210;///n+m=1365
int in[N];
int out[N];
struct Edge{
    int from, to, cap, flow;
    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct Dinic{
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[N];
    bool vis[N];
    int d[N];
    int cur[N];

    void init(int n)
    {
        this->n = n;
        for(int i = 0; i <= n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int cap)
    {
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BFS()
    {
        memset(vis, 0, sizeof vis);
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!Q.empty())
        {
            int x = Q.front();
            Q.pop();
            for(int i = 0; i < G[x].size(); i++)
            {
                Edge &e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)
                {
                    vis[e.to] = 1;
                    d[e.to] = d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }

    int DFS(int x,int a)
    {
        if(x==t||a==0) return a;
        int flow = 0, f;
        for(int &i = cur[x]; i < G[x].size(); i++)
        {
            Edge& e = edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow += f;
                edges[G[x][i]^1].flow -= f;
                flow += f;
                a -= f;
                if(a==0) break;
            }
        }
        return flow;
    }

    int Maxflow(int s,int t)
    {
        this->s = s, this->t = t;
        int flow = 0;
        while(BFS())
        {
            memset(cur, 0, sizeof cur);
            flow += DFS(s,INF);
        }
        return flow;
    }
};
int dw[N*N];
int main()
{
    int n, m;
    while(scanf("%d%d",&n,&m)==2)
    {
        Dinic dinic;
        dinic.init(n+1);
        int u, v, cap, flag;///1,n为普通源汇。
        int s = 0, t = n+1;///超级源汇。
        memset(dw, 0, sizeof dw);
        memset(in, 0, sizeof in);
        memset(out, 0, sizeof out);
        for(int i = 0; i<m; i++){
            scanf("%d%d%d%d",&u,&v,&cap,&flag);
            if(flag==1){
                dw[i] = cap;
                dinic.AddEdge(u,v,0);
                out[u]+=cap;
                in[v]+=cap;
            }else
            {
                dinic.AddEdge(u,v,cap);

            }
        }
        int sum = 0;
        for(int i = 1; i <= n; i++){
            if(in[i]>out[i]){
                dinic.AddEdge(s,i,in[i]-out[i]);
                sum += in[i]-out[i];
            }
            if(in[i]<out[i]){
                dinic.AddEdge(i,t,out[i]-in[i]);
            }
        }
        Dinic Tdinic = dinic;
        int flow;
        int lo = 0, hi = INF, mid;
        while(lo<hi){///最小流,二分上界,取最小值。
            mid = (lo+hi)/2;
            dinic = Tdinic;
            dinic.AddEdge(n,1,mid);
            flow = dinic.Maxflow(s,t);
            if(flow!=sum){
                lo = mid+1;
            }else
            {
                hi = mid;
            }
        }
        if(hi==lo){
            printf("Impossible\n"); continue;
        }
        printf("%d\n",hi);
        dinic = Tdinic;///注意复原,因为此时的dinic不是最小流为hi的时候的。要重新计算。
        dinic.AddEdge(n,1,hi);
        dinic.Maxflow(s,t);
        for(int i = 0; i < m; i++){
            if(i==m-1){
                printf("%d\n",dinic.edges[2*i].flow+dw[i]);
            }else
            {
                printf("%d ",dinic.edges[2*i].flow+dw[i]);
            }
        }
    }
    return 0;
}
时间: 2024-10-02 19:25:44

Flow construction SGU - 176 有源汇有上下界最小流 二分法和回流法的相关文章

loj #117. 有源汇有上下界最小流

题目链接 有源汇有上下界最小流,->上下界网络流 注意细节,边数组也要算上后加到SS,TT边. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 7 using namespace std; 8 9 const int N = 150010; 10 const int INF = 1e9;

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

有源汇带上下界最小流

LOj 模板 思路我就不多说了吧,和有源汇带上下界最大流一样,只不过这次是初流-残流网络最大流.关键这个模板题写的过程无限T一组,让我很绝望.在网上搜罗了很多代码,发现我有些地方可以优化. (1)跑dinic的时候可以使用当前弧优化 (2)在dinic过程中,如果rest已经等于0了,直接返回.不要不管他,感觉没什么影响,其实有的数据会卡死的(千万在边权更新之后再返回). #include<iostream> #include<cstdio> #include<cstring

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

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

有源汇有上下界最小流 (ZQU 1592)

这道题跟求最大流的时候差不多. 都是先构造可行流,然后判断是否可行, 可行的话,就利用残余流量,构造从汇点t跑到源点s的最大流, 如何求出答案呢. 在第一次求可行流的dinic后,跟求最大流的时候一样,从t到s是可行流的流量: 这个时候t到s的反向边,也就是s到t的流量就是t到s流的量(因为t到s定义权值为inf,要从这条边算出来,得用inf去减到这条边剩下的才是答案,有点麻烦) 所以反向边是个便捷的求法. 所以在第一次dinic之后,t到s的反向便的流量就是可行流的流量: 然后我们再从t到s跑

#117. 有源汇有上下界最小流

题目描述 n nn 个点,m mm 条边,每条边 e ee 有一个流量下界 lower(e) \text{lower}(e)lower(e) 和流量上界 upper(e) \text{upper}(e)upper(e),给定源点 s ss 与汇点 t tt,求源点到汇点的最小流. 输入格式 第一行两个正整数 n nn.m mm.s ss.t tt. 之后的 m mm 行,每行四个整数 s ss.t tt.lower \text{lower}lower.upper \text{upper}uppe

有上下界的网络流3-有源汇带上下界最小流SGU176

题目大意:有一个类似于工业加工生产的机器,起点为1终点为n,中间生产环节有货物加工数量限制,输入u v z c, 当c等于1时表示这个加工的环节必须对纽带上的货物全部加工(即上下界都为z),c等于0表示加工上界限制为z,下界为0,求节点1(起点)最少需要投放多少货物才能传送带正常工作. 解题思路:    1.直接 增设超级源点ss和超级汇点tt并连上附加边,对 当前图 求 无源汇带上下界可行流    2.将图的汇点sd连一条容量无限制的边到图的源点st,再求一遍 无源汇带上下界可行流    3.

ZOJ3229 Shoot the Bullet(有源汇的上下界最大流)

#pragma warning(disable:4996) #include <iostream> #include <cstring> #include <string> #include <vector> #include <cstdio> #include <algorithm> #include <cmath> #include <queue> #include <map> #include

HDU 3157 Crazy Circuits(有源汇上下界最小流)

HDU 3157 Crazy Circuits 题目链接 题意:一个电路板,上面有N个接线柱(标号1~N),还有两个电源接线柱 + -,给出一些线路,每个线路有一个下限值求一个可以让所有部件正常工作的总电流 没有则输出impossible 思路: 有源汇有上下界求最小流,建模方法为: 按无源汇先建图,跑超级源汇ss->tt一次,然后加入t->s,容量INF的边,在跑一次ss->tt,如果是满流,就有解,解为t->s边的当前流量 顺带写个最大流的,最大流就先把t->s加入直接跑