首页 > 代码库 > 有上下界的网络流2-有源汇带上下界网络流ZOJ3229

有上下界的网络流2-有源汇带上下界网络流ZOJ3229

ZOJ3229
题目大意:一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝可以和C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。

解题思路:
        1.增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为正无穷的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边。
        2.然后从汇点sd到源点st连一条流量最大限制为正无穷的边,这样当前图就成了一个无源无汇的循环图。可以加两个超级源点和超级汇点ss和tt,像上一篇日志那样求无源汇带上下界的可行流。
        3.如果求得有可行流,就把超级源点ss和超级汇点tt删掉,然后以st为源点、sd为汇点求当前图的最大流(因为上一步求得的只是可行流,里面还有很多边的流量未满)。当前求得的最大流即为解。如果为求得可行流则输出 -1 。

总结:对于有源汇带上下界的网络流求解,只需要从汇点连一条容量为正无穷的边到源点,构成一个无源汇的流量循环图,利用求无源汇带上下界网络流的方法求出一个可行流,再在求出的可行流上继续增广,最后求得的最大流即为有源汇带上下界的最大流。
贴个代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<cstring>
  7 #include<queue>
  8 #define maxn 0x7fffffff
  9 using namespace std;
 10 struct edge{int to,cap,rev;};
 11 struct a_goddess{int m,low;};
 12 int st=1,sd=2000,ss=0,tt=2001;
 13 vector <edge> E[2010];
 14 vector <a_goddess> day[400];
 15 int du[2010],ch[2010];
 16 int n,m;
 17 void Add_Edge(int from,int to,int low,int up){
 18      edge t;
 19      t.to=to,t.cap=up-low,t.rev=E[to].size();
 20      E[from].push_back(t);
 21      t.to=from,t.cap=0,t.rev=E[from].size()-1;
 22      E[to].push_back(t);
 23      du[from]-=low,du[to]+=low;
 24 }
 25 void init(){
 26      memset(du,0,sizeof(du));
 27      int G,C,D,T,L,R;
 28      for(int i=ss;i<=tt;i++) E[i].clear();
 29      for(int i=0;i<m;i++){
 30              scanf("%d",&G);
 31              Add_Edge(500+i,sd,G,maxn);
 32      }
 33      for(int i=1;i<=n;i++){
 34              day[i].clear();
 35              scanf("%d%d",&C,&D);
 36              Add_Edge(st,1+i,0,D);
 37              for(int j=0;j<C;j++){
 38                      scanf("%d%d%d",&T,&L,&R);
 39                      a_goddess t;t.m=T,t.low=L;
 40                      day[i].push_back(t);
 41                      Add_Edge(1+i,500+T,L,R);
 42              }
 43      }
 44 }
 45 bool bfs(int S,int T){
 46      memset(ch,-1,sizeof(ch));
 47      queue <int> que;
 48      que.push(S);ch[S]=0;
 49      while(que.size()){
 50                        int t=que.front();que.pop();
 51                        for(int i=0;i<E[t].size();i++){
 52                                if(ch[E[t][i].to]==-1 && E[t][i].cap){
 53                                                      ch[E[t][i].to]=ch[t]+1;
 54                                                      que.push(E[t][i].to);
 55                                }
 56                        }
 57      }
 58      return ch[T]>=0;
 59 }
 60 int dfs(int v,int T,int f){
 61     if(v==T) return f;
 62     int r=0;
 63     for(int i=0;i<E[v].size();i++){
 64             if (f==0) return r;
 65             if(ch[E[v][i].to]==ch[v]+1){
 66                                         int u=dfs(E[v][i].to,T,min(f,E[v][i].cap));
 67                                         f-=u,r+=u;
 68                                         E[v][i].cap-=u,E[E[v][i].to][E[v][i].rev].cap+=u;
 69             }
 70     }
 71     return r;
 72 }
 73 int dinic(int S,int T){
 74     int f=0;
 75     while(bfs(S,T)) f+=dfs(S,T,maxn);
 76     return f;
 77 }
 78 bool check(){
 79      int f=0;
 80      Add_Edge(sd,st,0,maxn);
 81      for(int i=st;i<=sd;i++){
 82              if(du[i]>0) {
 83                          f+=du[i];
 84                          Add_Edge(ss,i,0,du[i]);
 85              }
 86              if(du[i]<0) Add_Edge(i,tt,0,-du[i]);
 87      }
 88      return  (dinic(ss,tt)==f);
 89 }
 90 int main(){
 91     while(scanf("%d%d",&n,&m)!=EOF){
 92              init();
 93              if(check()){
 94                          for(int i=0;i<E[ss].size();i++){
 95                                  edge &t=E[ss][i];
 96                                  t.cap=0,E[t.to][t.rev].cap=0;
 97                          }
 98                          for(int i=0;i<E[tt].size();i++){
 99                                  edge &t=E[tt][i];
100                                  t.cap=0,E[t.to][t.rev].cap=0;
101                          }
102                          printf("%d\n",dinic(st,sd));
103                          int ans[1100];
104                          for(int i=1;i<=n;i++){
105                                  for(int j=0;j<E[1+i].size();j++){
106                                          int to=E[i+1][j].to-500;
107                                          if(0<=to && to<m){
108                                                   ans[to]=E[E[1+i][j].to][E[1+i][j].rev].cap;
109                                          }
110                                  }
111                                  for(int j=0;j<day[i].size();j++)
112                                          printf("%d\n",ans[day[i][j].m]+day[i][j].low);
113                          }
114              }
115              else{
116                   printf("%d\n",-1);
117              }
118              printf("\n");
119     }
120     return 0;
121 }

ps:建图的时候用了一个小技巧,因为天数n<=365,女神数m<=1000,所以第i天用点 (1+i) 表示,第i个女神用点(500+i)表示。

有上下界的网络流2-有源汇带上下界网络流ZOJ3229