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

这道题跟求最大流的时候差不多。

都是先构造可行流,然后判断是否可行,

可行的话,就利用残余流量,构造从汇点t跑到源点s的最大流,

如何求出答案呢。

在第一次求可行流的dinic后,跟求最大流的时候一样,从t到s是可行流的流量;

这个时候t到s的反向边,也就是s到t的流量就是t到s流的量(因为t到s定义权值为inf,要从这条边算出来,得用inf去减到这条边剩下的才是答案,有点麻烦)

所以反向边是个便捷的求法。

所以在第一次dinic之后,t到s的反向便的流量就是可行流的流量;

然后我们再从t到s跑最大流,看最大能跑掉多少,就是最后的答案。 (这里肯定不会影响可行流!,因为已经在第一次dinic的时候保证了可行(即sum==tmpp))

求第二次dinic的时候,要将s跟t之间的边给去掉。

  1 #include<cstdio>
  2 #include<string.h>
  3 #include<algorithm>
  4 #include<math.h>
  5 #include<queue>
  6 using namespace std;
  7 typedef long long ll;
  8 const ll maxn=51000;
  9 const ll inf=0x3f3f3f3f3f3f3f3f;
 10 ll vis[maxn];
 11 ll head[maxn],level[maxn];   //前者为邻接表必要数据,后者为dinic的层 数据
 12 ll in[maxn];  //一条边的初始流量(最低值  也就是定为下界)
 13 ll limit[maxn];  //limit为该点的流量   小于0的时候是流出多
 14 //ll bh[maxn];  //一条边的反向边的流量(正向边流出了多少,反向边就是多少)
 15                //所以用初始的下界流量+上这个方向边的流量就是最终答案;
 16                //那么为什么不直接正向呢???
 17                //废话 正向求比较麻烦嘛。  原本的值是可以流的量,正向边流出去后
 18                //那么答案就是可以流的量减去剩余的量就是答案;
 19                //所以还不如直接求反向呢
 20 ll num;  //邻接表
 21 void init()
 22 {
 23     num=-1  ;
 24     memset(head,-1,sizeof(head));
 25 }
 26 struct node
 27 {
 28     ll v,w,next;
 29 }G[maxn*12];
 30 ll bfs(ll s,ll t)
 31 {
 32     queue<ll>q;
 33     q.push(s);
 34     memset(level,-1,sizeof(level));
 35     level[s]=0;
 36     while(!q.empty()){
 37         ll u=q.front();
 38         q.pop();
 39         for(ll i=head[u];i!=-1;i=G[i].next){
 40             ll v=G[i].v;
 41             if(G[i].w>0&&level[v]==-1){
 42                 level[v]=level[u]+1;
 43                 q.push(v);
 44             }
 45         }
 46     }
 47    return level[t];
 48 }
 49 ll dfs(ll s,ll t,ll f)
 50 {
 51     if(s==t) return f;
 52     ll ans=0;
 53     for(ll i=vis[s];i!=-1;i=G[i].next){
 54         vis[s]=i;  //当前弧优化
 55         ll v=G[i].v;
 56         if(G[i].w>0&&level[s]+1==level[v]){
 57             ll d=dfs(v,t,min(G[i].w,f-ans));
 58             if(d>0){
 59                 G[i].w-=d;
 60                 G[i^1].w+=d;
 61                 ans+=d;
 62                 if(ans==f) return ans;
 63             }
 64         }
 65     }
 66    // if(ans==0) level[s]=0;
 67     return ans;
 68 }
 69 ll dinic(ll s,ll t)
 70 {
 71     ll ans=0;
 72     while(1){
 73         ll temp=bfs(s,t);
 74         if(temp==-1) break;
 75         memcpy(vis,head,sizeof(head));
 76         ans+=dfs(s,t,inf);
 77     }
 78     return ans;
 79 }
 80 void build(ll u,ll v,ll w)
 81 {
 82     num++;
 83     G[num].v=v;
 84     G[num].w=w;
 85     G[num].next=head[u];
 86     head[u]=num;
 87
 88     num++;
 89     G[num].v=u;
 90     G[num].w=0;
 91     G[num].next=head[v];
 92     head[v]=num;
 93 }
 94 int main()
 95 {
 96     init();
 97     ll n,m,s,t;
 98     scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
 99     for(ll i=1;i<=m;i++){
100         ll u,v,w1,w2;
101         scanf("%lld%lld%lld%lld",&u,&v,&w1,&w2);
102       //  in[u]=w1;
103         limit[u]-=w1;
104         limit[v]+=w1;
105         build(u,v,w2-w1);
106     }
107     ll S=0;ll T=n+1;
108     ll sum=0;
109     for(ll i=1;i<=n;i++){
110         if(limit[i]<0) build(i,T,-limit[i]);
111         if(limit[i]>0) build(S,i,limit[i]),sum+=limit[i];
112     }
113     build(t,s,inf);
114     //其实这里应该有上面的limit的变化 只是因为这里的w1为0,所以省略不写。
115     ll tmpp=dinic(S,T);
116     if(tmpp!=sum) printf("please go home to sleep\n");
117     else{
118         ll tmp1=G[num].w;
119         G[num].w=G[num-1].w=0;
120         ll tmp2=dinic(t,s);
121         printf("%lld\n",tmp1-tmp2);
122     }
123     return 0;
124 }

原文地址:https://www.cnblogs.com/pangbi/p/11808944.html

时间: 2024-09-29 05:41:29

有源汇有上下界最小流 (ZQU 1592)的相关文章

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

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的边删掉(可以使流量等于

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

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

#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加入直接跑