ZOJ 3229 Shoot the Bullet(有源汇上下界最大流)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442
题目大意:
一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。
解题思路:
有源汇带上下界的最大流,我也是第一次写。
说下建图:
①源点S到第i天流量为D[i]的边(上界为D[i],下界为0)
②第i天向女孩连流量为r-l的边(上界为r,下界为l)
③女孩向汇点T连流量为G[i]的边(上界为INF,下界为G[i])
④汇点向源点连流量为INF的边,使其变成无源汇图。(上界为INF,下界为0)
⑤out[i]为i点的出边下界和,in[i]为i点的入边下界和,若in[i]-out[i]>0,则从附加源点SS向i连流量为in[i]-out[i]的边,
若in[i]-out[i]<0,则从i点向附加汇点TT连流量为out[i]-in[i]的边。
具体求法,就是先求出改图是否存在可行流,即求有源汇上下界可行流,通过从T->连流量为INF的边即可将图变为无源汇图,这样就可以转换为无源汇上下界可行流求解。
若存在可行流,则再求一次S->T的最大流即为答案。
(这句话从网上找的)
为什么呢?因为第一次SS->TT只是求得所有满足下界的流量,而残留网络(S,T)路上还有许多自由流(没有和超级源点和超级汇点连接的边)没有流满,所有最终得到的ans=(第一次流满下界的流+第二次能流通的自由流)。
说起来ZOJ真是害我不浅啊,今天刚开始写上下界的题目,ZOJ 2314 low数组开小了一直TLE。。。。 然后这题忘记输出空行一直WA。。。 然后我每次都以为是自己写的问题debug好几个小时,难受啊。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #define LL long long 9 #define pii pair<int,int> 10 #define pll pair<long long,long long> 11 #define rep(i,a,b) for(int i=a;i<=b;i++) 12 #define per(i,a,b) for(int i=a;i>=b;i--) 13 #define FAST_IO ios::sync_with_stdio(false);cin.tie(0); 14 #define bug cout<<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"<<endl; 15 #define bugc(_) cout << (#_) << " = " << (_) << endl; 16 using namespace std; 17 const int N=2e3+5; 18 const int M=1e5+5; 19 const int INF=0x3f3f3f3f; 20 21 struct node{ 22 int to,next,flow; 23 }edge[M*2]; 24 25 int cnt,st,en; 26 int head[N],dep[N],out[N],in[N],low[M],G[N],D[N]; 27 28 void init(){ 29 cnt=2; 30 memset(head,0,sizeof(head)); 31 memset(out,0,sizeof(out)); 32 memset(in,0,sizeof(in)); 33 } 34 35 void link(int u,int v,int flow){ 36 edge[cnt]=node{v,head[u],flow}; 37 head[u]=cnt++; 38 edge[cnt]=node{u,head[v],0}; 39 head[v]=cnt++; 40 } 41 42 int bfs(){ 43 memset(dep,0,sizeof(dep)); 44 dep[st]=1; 45 queue<int>q; 46 q.push(st); 47 while(!q.empty()){ 48 int u=q.front(); 49 q.pop(); 50 for(int i=head[u];i;i=edge[i].next){ 51 node t=edge[i]; 52 if(t.flow&&!dep[t.to]){ 53 dep[t.to]=dep[u]+1; 54 q.push(t.to); 55 } 56 } 57 } 58 return dep[en]; 59 } 60 61 int dfs(int u,int fl){ 62 if(en==u) return fl; 63 int tmp=0; 64 for(int i=head[u];i&&fl;i=edge[i].next){ 65 node &t=edge[i]; 66 if(t.flow&&dep[t.to]==dep[u]+1){ 67 int x=dfs(t.to,min(t.flow,fl)); 68 if(x>0){ 69 tmp+=x; 70 fl-=x; 71 t.flow-=x; 72 edge[i^1].flow+=x; 73 } 74 } 75 } 76 if(!tmp) dep[u]=-2; 77 return tmp; 78 } 79 80 int dinic(int S,int T){ 81 st=S,en=T; 82 int ans=0; 83 while(bfs()){ 84 while(int d=dfs(st,INF)) 85 ans+=d; 86 } 87 return ans; 88 } 89 90 int main(){ 91 int n,m; 92 while(~scanf("%d%d",&n,&m)){ 93 init(); 94 int S=0,T=n+m+1,SS=n+m+2,TT=n+m+3; 95 for(int i=1;i<=m;i++) scanf("%d",&G[i]); 96 int num=0,sum=0; 97 for(int i=1;i<=n;i++){ 98 int C; 99 scanf("%d%d",&C,&D[i]); 100 for(int j=1;j<=C;j++){ 101 int id,l,r; 102 scanf("%d%d%d",&id,&l,&r); 103 id++; 104 link(i,id+n,r-l); 105 low[++num]=l; 106 out[i]+=l; 107 in[id+n]+=l; 108 } 109 } 110 link(T,S,INF); 111 for(int i=1;i<=n;i++){ 112 link(S,i,D[i]); //D[i]是上限,下限为0,因为每天最多拍D[i]张照片 113 } 114 for(int i=1;i<=m;i++){ 115 link(i+n,T,INF);//G[i]是下限,因为每个人至少要G[i]张照片 116 in[T]+=G[i]; 117 out[i+n]+=G[i]; 118 } 119 for(int i=0;i<=n+m+1;i++){ 120 int tmp=in[i]-out[i]; 121 if(tmp>0) link(SS,i,tmp),sum+=tmp; 122 else if(tmp<0) link(i,TT,-tmp); 123 } 124 int ans=dinic(SS,TT); 125 if(ans==sum){ 126 ans=dinic(S,T); 127 printf("%d\n",ans); 128 for(int i=1;i<=num;i++){ 129 printf("%d\n",edge[i*2+1].flow+low[i]); 130 } 131 } 132 else 133 puts("-1"); 134 puts(""); 135 } 136 return 0; 137 }