有源汇有上下界最小流 (ZQU 1592)
Posted pangbi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有源汇有上下界最小流 (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 "); 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 ",tmp1-tmp2); 122 } 123 return 0; 124 }
以上是关于有源汇有上下界最小流 (ZQU 1592)的主要内容,如果未能解决你的问题,请参考以下文章
HDU 3157 Crazy Circuits (有源汇上下界最小流)