zoj3229 Shoot the Bullet (有源汇最大流)

题目大意:文文要给幻想乡的女♂孩子们拍照,一共n天,m个女♂孩子,每天文文至多拍D[i]张照片,每个女♂孩子总共要被文文至少拍G[i]次。在第i天,文文可以拍c[i]个女♂孩子,c[i]个女♂孩子中每个女♂孩子在当天被拍的次数是[li,ri],求最多可以拍多少张照片,以及每天每个可以拍的女♂孩子被拍了多少张照片。

然后就很容易想到一个网络流模型:

n天放左部,m个人放右部,源点向每天连边,容量D[i],m个人向汇点连边,容量G[i],左部向右部连边,容量是它们的rili,这是有源汇的上下界最大流问题…

首先先做一个有源汇上下界可行流,然后再从残量网络上跑一个s到e的最大流就是答案。

题解

首先S向每天连[0,day]的边,每一天与拍照的妹子连[l,r]的边,每个女孩和汇连[g,oo]的边

对于有源和汇的上下界网络流,只要T到S连一条[0,inf]的边,那么原图成为一个无源点汇点的循环流图,那么新建SS和TT,向每个点连边(即每一点的入流为正则源向其连,否则向汇连),求SS到TT的最大流判断满流

判断有解之后求最大流再做一次S到T的最大流

同时要去掉原来T到S的[0,inf]的边

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #define inf 1000000000
  5 using namespace std;
  6 inline int read()
  7 {
  8     int x=0,f=1;char ch=getchar();
  9     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 10     while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();}
 11     return x*f;
 12 }
 13 int n,m,cnt,ans;
 14 int head[1505],cur[1505],h[1505],q[1505],in[1505];
 15 int day[505],low[100005];
 16 struct data{int to,next,v;}e[100005];
 17 void ins(int u,int v,int w)
 18 {e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].v=w;}
 19 void insert(int u,int v,int w)
 20 {ins(u,v,w);ins(v,u,0);}
 21 bool bfs(int S,int T)
 22 {
 23      memset(h,-1,sizeof(h));
 24      int t=0,w=1;q[0]=S;h[S]=0;
 25      while(t!=w)
 26      {
 27           int now=q[t];t++;
 28           for(int i=head[now];i;i=e[i].next)
 29               if(e[i].v&&h[e[i].to]==-1)
 30               {
 31                    h[e[i].to]=h[now]+1;
 32                    q[w++]=e[i].to;
 33               }
 34      }
 35      if(h[T]==-1)return 0;
 36      return 1;
 37 }
 38 int dfs(int x,int f,int T)
 39 {
 40      if(x==T)return f;
 41      int w,used=0;
 42      for(int i=cur[x];i;i=e[i].next)
 43           if(h[e[i].to]==h[x]+1)
 44           {
 45                w=f-used;w=dfs(e[i].to,min(e[i].v,w),T);
 46                e[i].v-=w;if(e[i].v)cur[x]=i;e[i^1].v+=w;
 47                used+=w;if(used==f)return f;
 48           }
 49      if(!used)h[x]=1;
 50      return used;
 51 }
 52 void dinic(int S,int T)
 53 {while(bfs(S,T)){for(int i=0;i<=n+m+3;i++)cur[i]=head[i];dfs(S,inf,T);}}
 54 bool jud()
 55 {
 56      int SS=n+m+2,TT=n+m+3;
 57      for(int i=0;i<=n+m+1;i++)
 58      {
 59           if(in[i]>0)insert(SS,i,in[i]);
 60           if(in[i]<0)insert(i,TT,-in[i]);
 61      }
 62      dinic(SS,TT);
 63      for(int i=head[SS];i;i=e[i].next)
 64          if(e[i].v)return 0;
 65      return 1;
 66 }
 67 int main()
 68 {
 69      while(scanf("%d%d",&n,&m)!=EOF)
 70      {
 71           cnt=1;int S=0,T=n+m+1;
 72           ans=0;
 73           memset(head,0,sizeof(head));
 74           memset(in,0,sizeof(in));
 75           for(int i=1;i<=m;i++)
 76           {
 77                int g=read();
 78                in[T]+=g;
 79                in[i+n]-=g;
 80           }
 81           int ed=0;
 82           for(int i=1;i<=n;i++)
 83           {
 84                int x=read();day[i]=read();
 85                for(int j=1;j<=x;j++)
 86                {
 87                    int num=read();
 88                    low[++ed]=read();int r=read();
 89                    insert(i,n+num+1,r-low[ed]);
 90                    in[i]-=low[ed];
 91                    in[n+num+1]+=low[ed];
 92                }
 93           }
 94           for(int i=1;i<=n;i++)insert(S,i,day[i]);
 95           for(int i=1;i<=m;i++)insert(i+n,T,inf);
 96           insert(T,S,inf);
 97           if(jud())
 98           {
 99                dinic(S,T);insert(T,S,-inf);//掉原来T到S的[0,inf]的边
100                for(int i=head[S];i;i=e[i].next)
101                    ans+=e[i^1].v;
102                printf("%d
",ans);
103                for(int i=1;i<=ed;i++)
104                    printf("%d
",e[(i<<1)^1].v+low[i]);
105           }
106           else printf("-1
");
107           puts("");
108      }
109      return 0;
110 }

然后来建图:引入源点S汇点T,n天一天一点,m个少女每人一点。S到每一天连一条边,上界为Dt,下界为0。每个少女到汇点连一条边,上界为无穷大,下界为Gi。对每一天,连一条边到所有要拍照的少女,下界为L,上界为R。然后引入超级源点SS,超级汇点TT,像无源无汇上下界可行流那样建边,然后T到S连一条边,容量为无穷大。最后从源点S到汇点T跑一遍最大流就是答案,每条边容量的取法和无源无汇上下界可行流一样。

小证明:原图为什么是那样建的我就不解释了。

至于加入超级源点和超级汇点之后为什么要连一条边T→S呢,因为根据无源无汇上下界可行流的做法,原图无源无汇,是应该有一个环的,而现在没有……

之后再从S到T跑一遍,会不会影响之前的流量下界呢?满足下界是来自于点与超级源点和超级汇点之间的边,如果我们把这两个点删掉,边的容量就没法变了,也就不会影响到流量下界。而我没有删掉,是因为若求出可行解之后,超级源点和超级汇点关联的边都应该是满流量的,再跑最大流不可能经过这两个点。

那么跑可行流的时候,和跑最大流的时候,流量是分开算的,那为什么不用加上原来可行流的流量呢?因为原先我们连了一条边T→S,根据无源无汇图中,每个点的入流等于出流,跑可行流的流量已经存在了T→S这条边中,跑最大流就时候,T→S的反向边就有了可行流的容量。所以跑最大流的时候会加上可行流的容量,就不用再加一遍了。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <queue>
  5 using namespace std;
  6 
  7 const int MAXN = 2010;
  8 const int MAXE = 1000010;
  9 const int INF = 0x3fff3fff;
 10 
 11 struct SAP {
 12     int head[MAXN], gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN];
 13     int to[MAXE], next[MAXE], flow[MAXE], cap[MAXE];
 14     int n, ecnt, st, ed;
 15 
 16     void init() {
 17         memset(head, 0, sizeof(head));
 18         ecnt = 2;
 19     }
 20 
 21     void add_edge(int u, int v, int c) {
 22         to[ecnt] = v; cap[ecnt] = c; flow[ecnt] = 0; next[ecnt] = head[u]; head[u] = ecnt++;
 23         to[ecnt] = u; cap[ecnt] = 0; flow[ecnt] = 0; next[ecnt] = head[v]; head[v] = ecnt++;
 24         //printf("%d->%d %d
", u, v, c);
 25     }
 26 
 27     void bfs() {
 28         memset(dis, 0x3f, sizeof(dis));
 29         queue<int> que; que.push(ed);
 30         dis[ed] = 0;
 31         while(!que.empty()) {
 32             int u = que.front(); que.pop();
 33             ++gap[dis[u]];
 34             for(int p = head[u]; p; p = next[p]) {
 35                 int &v = to[p];
 36                 if(cap[p ^ 1] > flow[p ^ 1] && dis[v] > n) {
 37                     dis[v] = dis[u] + 1;
 38                     que.push(v);
 39                 }
 40             }
 41         }
 42     }
 43 
 44     int Max_flow(int ss, int tt, int nn) {
 45         st = ss, ed = tt, n = nn;
 46         int ans = 0, minFlow = INF, u;
 47         for(int i = 0; i <= n; ++i) {
 48             cur[i] = head[i];
 49             gap[i] = 0;
 50         }
 51         u = pre[st] = st;
 52         bfs();
 53         while(dis[st] < n) {
 54             bool flag = false;
 55             for(int &p = cur[u]; p; p = next[p]) {
 56                 int &v = to[p];
 57                 if(cap[p] > flow[p] && dis[u] == dis[v] + 1) {
 58                     flag = true;
 59                     minFlow = min(minFlow, cap[p] - flow[p]);
 60                     pre[v] = u;
 61                     u = v;
 62                     if(u == ed) {
 63                         ans += minFlow;
 64                         while(u != st) {
 65                             u = pre[u];
 66                             flow[cur[u]] += minFlow;
 67                             flow[cur[u] ^ 1] -= minFlow;
 68                         }
 69                         minFlow = INF;
 70                     }
 71                     break;
 72                 }
 73             }
 74             if(flag) continue;
 75             int minDis = n - 1;
 76             for(int p = head[u]; p; p = next[p]) {
 77                 int &v = to[p];
 78                 if(cap[p] > flow[p] && dis[v] < minDis) {
 79                     minDis = dis[v];
 80                     cur[u] = p;
 81                 }
 82             }
 83             if(--gap[dis[u]] == 0) break;
 84             ++gap[dis[u] = minDis + 1];
 85             u = pre[u];
 86         }
 87         return ans;
 88     }
 89 } G;
 90 
 91 int n, m, c, d, l, r, x;
 92 int f[MAXN], down[MAXE], id[MAXE];
 93 
 94 int main() {
 95     while(scanf("%d%d", &n, &m) != EOF) {
 96         G.init();
 97         memset(f, 0, sizeof(f));
 98         int s = n + m + 1, t = s + 1;
 99         int ss = t + 1, tt = ss + 1;
100         for(int i = 1; i <= m; ++i) {
101             scanf("%d", &x);
102             f[i] -= x;
103             f[t] += x;
104             G.add_edge(i, t, INF);
105         }
106         int cnt = 0;
107         for(int i = 1; i <= n; ++i) {
108             scanf("%d%d", &c, &d);
109             G.add_edge(s, i + m, d);
110             while(c--) {
111                 scanf("%d%d%d", &x, &l, &r);
112                 f[i + m] -= l;
113                 f[x + 1] += l;
114                 down[++cnt] = l; id[cnt] = G.ecnt;
115                 G.add_edge(i + m, x + 1, r - l);
116             }
117         }
118         int sum = 0;
119         for(int i = 1; i <= t; ++i) {
120             if(f[i] > 0) sum += f[i], G.add_edge(ss, i, f[i]);
121             else G.add_edge(i, tt, -f[i]);
122         }
123         G.add_edge(t, s, INF);
124         if(sum != G.Max_flow(ss, tt, tt)) puts("-1");
125         else {
126             printf("%d
", G.Max_flow(s, t, tt));
127             for(int i = 1; i <= cnt; ++i) printf("%d
", down[i] + G.flow[id[i]]);
128         }
129         puts("");
130     }
131 }
原文地址:https://www.cnblogs.com/agenthtb/p/7689525.html