有上下界的網絡流問題


此類問題可以分為三小類問題:

一、無源匯有上下界最大流

二、有源匯有上下界最大流

三、有源匯有上下界最小流

 

1、無源匯有上下界最大流

 題目鏈接: sgu194 Reactor Cooling

 題目大意:給n個點,及m根pipe,每根pipe用來流躺液體的,單向的,每時每刻每根pipe流進來的物質要等於流出去的物質,要使得m條pipe組成一個循環體,里面流躺物質。並且滿足每根pipe一定的流量限制,范圍為[Li,Ri].即要滿足每時刻流進來的不能超過Ri(最大流問題),同時最小不能低於Li。

解題思路:O(-1)。

 

 建圖模型: 以前寫的最大流默認的下界為0,而這里的下界卻不為0,所以我們要進行再構造讓每條邊的下界為0,這樣做是為了方便處理。對於每根管子有一個上界容量up和一個下界容量low,我們讓這根管子的容量下界變為0,上界為up-low。可是這樣做了的話流量就不守恆了,為了再次滿足流量守恆,即每個節點"入流=出流”,我們增設一個超級源點st和一個超級終點sd。我們開設一個數組du[]來記錄每個節點的流量情況。

du[i]=in[i](i節點所有入流下界之和)-out[i](i節點所有出流下界之和)。

當du[i]大於0的時候,st到i連一條流量為du[i]的邊。

當du[i]小於0的時候,i到sd連一條流量為-du[i]的邊。

最后對(st,sd)求一次最大流即可,當所有附加邊全部滿流時(即maxflow==所有du[]>0之和),有可行解。

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cmath>
  4 #include <algorithm>
  5 #include <cstring>
  6 using namespace std;
  7 
  8 const int mn=22222;
  9 const int mm=1000000;
 10 const int oo=0x3fffffff;
 11 int node, st, sd, edge, Edge;
 12 int reach[mm], flow[mm], next[mm];
 13 int head[mn], work[mn], dis[mn], que[mn];
 14 int du[mm], ans[mm], id[mm], dn[mm];
 15 
 16 inline void init(int _node, int _st, int _sd)
 17 {
 18     node=_node, st=_st, sd=_sd;
 19     for(int i=0; i<node; i++)
 20         head[i]=-1;
 21     edge=0;
 22 }
 23 
 24 inline void addedge(int u, int v, int c1, int c2, int ID)
 25 {
 26     id[edge]=ID, reach[edge]=v, flow[edge]=c1, next[edge]=head[u],head[u]=edge++;
 27     id[edge]=0, reach[edge]=u, flow[edge]=c2, next[edge]=head[v],head[v]=edge++;
 28 }
 29 
 30 bool bfs()
 31 {
 32     int u, v, l=0, h=0;
 33     for(int i=0; i<node; i++) dis[i]=-1;
 34     que[l++]=st;
 35     dis[st]=0;
 36     while(l!=h)
 37     {
 38         u=que[h++];
 39         if(h==mn) h=0;
 40         for(int i=head[u]; i>=0; i=next[i])
 41         {
 42             v=reach[i];
 43             if(flow[i]&&dis[v]<0)
 44             {
 45                 dis[v]=dis[u]+1;
 46                 que[l++]=v;
 47                 if(l==mn) l=0;
 48                 if(v==sd) return true;
 49             }
 50         }
 51     }
 52     return false;
 53 }
 54 
 55 int dfs(int u, int exp)
 56 {
 57     if(u==sd) return exp;
 58     for(int &i=work[u]; i>=0; i=next[i])
 59     {
 60         int v=reach[i], tp;
 61         if(flow[i]&&dis[v]==dis[u]+1&&(tp=dfs(v,min(flow[i],exp)))>0)
 62         {
 63             flow[i]-=tp;
 64             flow[i^1]+=tp;
 65             return tp;
 66         }
 67     }
 68     return 0;
 69 }
 70 
 71 void Dinic()
 72 {
 73     while(bfs())
 74     {
 75         for(int i=0; i<node; i++) work[i]=head[i];
 76         while(dfs(st,oo));
 77     }
 78 }
 79 
 80 int main()
 81 {
 82     int n,m;
 83     while(~scanf("%d%d",&n,&m))
 84     {
 85         init(n+2,0,n+1);
 86         for(int i=1; i<=m; i++)
 87         {
 88             int u, v, down, up;
 89             scanf("%d%d%d%d",&u,&v,&down,&up);
 90             addedge(u,v,up-down,0,i);
 91             du[u]-=down;
 92             du[v]+=down;
 93             dn[i]=down;
 94         }
 95         Edge=edge;
 96         for(int i=1; i<=n; i++)
 97         {
 98             if(du[i]>0) addedge(st,i,du[i],0,0);
 99             if(du[i]<0) addedge(i,sd,-du[i],0,0);
100         }
101         Dinic();
102         bool flag=true;
103         for(int i=head[st]; i>=0; i=next[i])
104             if(flow[i]>0)
105             {
106                 flag=false;
107                 break;
108             }
109         if(!flag) puts("NO");
110         else
111         {
112             puts("YES");
113             for(int i=0; i<Edge; i++) ans[id[i]]=flow[i^1];
114             for(int i=1; i<=m; i++)
115                 printf("%d\n",ans[i]+dn[i]);
116         }
117     }
118     return 0;
119 }

 

2、有源匯有上下界的最大流

題目鏈接:zoj3229 Shoot the Bullet

題目大意:一個屌絲給m個女神拍照,計划拍照n天,每一天屌絲最多個C個女神拍照,每天拍照數不能超過D張,而且給每個女神i拍照有數量限制[Li,Ri],對於每個女神n天的拍照總和不能超過Gi,如果有解求屌絲最多能拍多少張照,並求每天給對應女神拍多少張照;否則輸出-1。

解題思路:增設一源點st,匯點sd,st到第i天連一條上界為Di下界為0的邊,每個女神到匯點連一條下界為Gi上界為oo的邊,對於每一天,當天到第i個女孩連一條[Li,Ri]的邊。

 

建圖模型:源點s,終點d。超級源點ss,超級終點dd。首先判斷是否存在滿足所有邊上下界的可行流,方法可以轉化成無源匯有上下界的可行流問題。怎么轉換呢?

增設一條從d到s沒有下界容量為無窮的邊,那么原圖就變成了一個無源匯的循環流圖。接下來的事情一樣,超級源點ss連i(du[i]>0),i連超級匯點(du[i]<0),

對(ss,dd)進行一次最大流,當maxflow等於所有(du[]>0)之和時,有可行流,否則沒有。

當有可行流時,刪除超級源點ss和超級終點dd,再對(s,d)進行一次最大流,此時得到的maxflow則為題目的解。為什么呢?因為第一次maxflow()只是求得所有滿足下界的流量,而殘留網絡(s,d)路上還有許多自由流(沒有和超級源點和超級匯點連接的邊)沒有流滿,所有最終得到的maxflow=(第一次流滿下界的流+第二次能流通的自由流)。

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <queue>
  5 #include <algorithm>
  6 using namespace std;
  7 
  8 const int mn=2222;
  9 const int mm=1000000;
 10 const int oo=100000000;
 11 int node, st, sd, edge;
 12 int reach[mm], flow[mm], next[mm];
 13 int head[mn], work[mn], dis[mn], que[mn];
 14 int du[mn], dn[444][1111], id[444][1111];
 15 
 16 inline void init(int _node, int _st, int _sd)
 17 {
 18     node=_node, st=_st, sd=_sd;
 19     for(int i=0; i<node; i++)
 20         head[i]=-1, du[i]=0;
 21     edge=0;
 22 }
 23 
 24 inline void addedge(int u, int v, int c1, int c2)
 25 {
 26     reach[edge]=v, flow[edge]=c1, next[edge]=head[u],head[u]=edge++;
 27     reach[edge]=u, flow[edge]=c2, next[edge]=head[v],head[v]=edge++;
 28 }
 29 
 30 bool bfs()
 31 {
 32     int u, v, l=0, h=0;
 33     for(int i=0; i<node; i++) dis[i]=-1;
 34     que[l++]=st;
 35     dis[st]=0;
 36     while(l!=h)
 37     {
 38         u=que[h++];
 39         if(h==mn) h=0;
 40         for(int i=head[u]; i>=0; i=next[i])
 41         {
 42             v=reach[i];
 43             if(flow[i]&&dis[v]<0)
 44             {
 45                 dis[v]=dis[u]+1;
 46                 que[l++]=v;
 47                 if(l==mn) l=0;
 48                 if(v==sd) return true;
 49             }
 50         }
 51     }
 52     return false;
 53 }
 54 
 55 int dfs(int u, int exp)
 56 {
 57     if(u==sd) return exp;
 58     for(int &i=work[u]; i>=0; i=next[i])
 59     {
 60         int v=reach[i], tp;
 61         if(flow[i]&&dis[v]==dis[u]+1&&(tp=dfs(v,min(flow[i],exp)))>0)
 62         {
 63             flow[i]-=tp;
 64             flow[i^1]+=tp;
 65             return tp;
 66         }
 67     }
 68     return 0;
 69 }
 70 
 71 int Dinic()
 72 {
 73     int max_flow=0, flow;
 74     while(bfs())
 75     {
 76         for(int i=0; i<node; i++) work[i]=head[i];
 77         while(flow=dfs(st,oo)) max_flow+=flow;
 78     }
 79     return max_flow;
 80 }
 81 
 82 int main()
 83 {
 84     int n, m, g, l, r, c, d;
 85     while(scanf("%d%d",&n,&m)!=EOF)
 86     {
 87         init(n+m+2,n+m,n+m+1);
 88         for(int i=0; i<m; i++)
 89         {
 90             scanf("%d",&g);
 91             du[sd]+=g;
 92             du[i+n]-=g;
 93             addedge(i+n,sd,oo-g,0);
 94         }
 95         memset(id,0,sizeof(id));
 96         for(int i=0; i<n; i++)
 97         {
 98             scanf("%d%d",&c,&d);
 99             addedge(st,i,d,0);
100             for(int j=0; j<c; j++)
101             {
102                 scanf("%d%d%d",&g,&l,&r);
103                 du[i]-=l;
104                 du[g+n]+=l;
105                 dn[i][g]=l;
106                 addedge(i,g+n,r-l,0);
107                 id[i][g]=edge-1;
108             }
109         }
110         addedge(sd,st,oo,0);
111         st=node, sd=node+1, node+=2;
112         head[st]=head[sd]=-1; ///!!
113         int sum=0;
114         for(int i=0; i<node-2; i++)
115         {
116             if(du[i]>0) sum+=du[i], addedge(st,i,du[i],0);
117             else addedge(i,sd,-du[i],0);
118         }
119         int maxflow=Dinic();
120         if(maxflow!=sum)  puts("-1");
121         else
122         {
123             head[st]=head[sd]=-1, node-=2;
124             st=node-2, sd=node-1;
125             maxflow=Dinic();
126             printf("%d\n",maxflow);
127             for(int i=0; i<n; i++)
128             {
129                 for(int j=0; j<m; j++)
130                    if(id[i][j])
131                      printf("%d\n",flow[id[i][j]]+dn[i][j]);
132             }
133         }
134         puts("");
135     }
136     return 0;
137 }

 

 

3、有源匯有上下界的最小流

題目鏈接:   sgu176  Flow construction

題目大意:有一個類似於工業加工生產的機器,起點為1終點為n,中間生產環節有貨物加工數量限制,輸出u v z c, 當c等於1時表示這個加工的環節必須對紐帶上的貨物全部加工(即上下界都為z),c等於0表示加工沒有上界限制,下界為0,求節點1(起點)最少需要投放多少貨物才能傳送帶正常工作。

解題思路:

   1、du[i]表示i節點的入流之和與出流之和的差。 

   2、增設超級源點st和超級匯點sd,連(st,du[i](為正)),(-du[i](為負),sd)。 ///增設超級源點和超級匯點,因為網絡中規定不能有弧指向st,也不能有流量流出sd

   3、做一次maxflow()。

   4、源點(Sd)和起點(St)連一條容量為oo的邊。

   5、再做一次maxflow()。

   6、當且僅當所有附加弧滿載時有可行流,最后答案為flow[(Sd->St)^1],St到Sd最大流就是Sd到St最小流。

 

建圖模型:同樣轉換成先求無源匯有上下界的可行流,先添加一條d到s容量為無窮的邊,這里求最小流很容易讓人產生歧路,為什么呢?當所有邊滿足下界條件並且能量守恆時,這時候求得的最大流不就是最小流么。這樣是錯誤了,我開始了在這揣測了良久。

下面來看個例子:

這樣求得的最小流為200,而實際的可行最小流解只需100。

問題出在原圖中存在環(循環流),而我們沒有利用,導致流增大了。

解決方法:先不增加d->s容量為無窮的邊,進行一次maxflow(),如果還沒有滿流,則加一條(d,s)容量為無窮的邊,再進行一次maxflow(),當且僅當所有附加弧滿載時,有可行解,解為flow[(d->s)^1](即d到s的后悔邊權值)。

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cmath>
  4 #include <algorithm>
  5 #include <cstring>
  6 using namespace std;
  7 
  8 const int mn=111;
  9 const int mm=11111;
 10 const int oo=0x3fffffff;
 11 int node, st, sd, edge, St, Sd, Edge;
 12 int reach[mm], flow[mm], next[mm];
 13 int head[mn], work[mn], dis[mn], que[mn];
 14 int du[mm], ans[mm], id[mm], dn[mm];
 15 
 16 inline void init(int _node, int _st, int _sd)
 17 {
 18     node=_node, st=_st, sd=_sd;
 19     for(int i=0; i<node; i++)
 20         head[i]=-1, du[i]=0;
 21     edge=0;
 22 }
 23 
 24 inline void addedge(int u, int v, int c1, int c2, int ID)
 25 {
 26     id[edge]=ID, reach[edge]=v, flow[edge]=c1, next[edge]=head[u],head[u]=edge++;
 27     id[edge]=0, reach[edge]=u, flow[edge]=c2, next[edge]=head[v],head[v]=edge++;
 28 }
 29 
 30 bool bfs()
 31 {
 32     int u, v, l=0, h=0;
 33     for(int i=0; i<node; i++) dis[i]=-1;
 34     que[l++]=st;
 35     dis[st]=0;
 36     while(l!=h)
 37     {
 38         u=que[h++];
 39         if(h==mn) h=0;
 40         for(int i=head[u]; i>=0; i=next[i])
 41         {
 42             v=reach[i];
 43             if(flow[i]&&dis[v]<0)
 44             {
 45                 dis[v]=dis[u]+1;
 46                 que[l++]=v;
 47                 if(l==mn) l=0;
 48                 if(v==sd) return true;
 49             }
 50         }
 51     }
 52     return false;
 53 }
 54 
 55 int dfs(int u, int exp)
 56 {
 57     if(u==sd) return exp;
 58     for(int &i=work[u]; i>=0; i=next[i])
 59     {
 60         int v=reach[i], tp;
 61         if(flow[i]&&dis[v]==dis[u]+1&&(tp=dfs(v,min(flow[i],exp)))>0)
 62         {
 63             flow[i]-=tp;
 64             flow[i^1]+=tp;
 65             return tp;
 66         }
 67     }
 68     return 0;
 69 }
 70 
 71 void Dinic()
 72 {
 73     int max_flow=0, flow;
 74     while(bfs())
 75     {
 76         for(int i=0; i<node; i++) work[i]=head[i];
 77         while(flow=dfs(st,oo)) max_flow+=flow;
 78     }
 79 }
 80 
 81 int main()
 82 {
 83     int n, m;
 84     while(~scanf("%d%d",&n,&m))
 85     {
 86         init(n+1,1,n);
 87         for(int i=1; i<=m; i++)
 88         {
 89             int u, v, c, k;
 90             scanf("%d%d%d%d",&u,&v,&c,&k);
 91             if(k) du[u]-=c, du[v]+=c, ans[i]=c;
 92             else addedge(u,v,c,0,i);
 93         }
 94         St=st, Sd=sd, Edge=edge;
 95         st=node, sd=node+1, node+=2;    ///增設超級源點和超級匯點,因為網絡中規定不能有弧指向st,也不能有流量流出sd
 96         head[st]=head[sd]=-1;
 97         for(int i=1; i<=n; i++)
 98         {
 99             if(du[i]>0) addedge(st,i,du[i],0,0);
100             if(du[i]<0) addedge(i,sd,-du[i],0,0);
101         }
102         Dinic();
103         addedge(Sd,St,oo,0,0);
104         Dinic();
105         bool flag=true;
106         for(int i=head[st]; i>=0; i=next[i])
107             if(flow[i]>0)   ///當且僅當附加弧達到滿負載有可行流
108             {
109                 flag=false;
110                 break;
111             }
112         if(!flag)
113             puts("Impossible");
114         else
115         {
116             int res=0, i;
117             for(i=head[Sd]; i>=0; i=next[i])
118               if(reach[i]==St) break;
119             res=flow[i^1];
120             printf("%d\n",res);
121             for(i=0; i<Edge; i++) ans[id[i]]=flow[i^1];
122             for(i=1; i<=m; i++)
123             {
124                 if(i!=m) printf("%d ",ans[i]);
125                 else printf("%d\n",ans[i]);
126             }
127         }
128     }
129     return 0;
130 }

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM