1. 程式人生 > >ZOJ 3229 Shoot the Bullet(有源匯上下界最大流)

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=(第一次流滿下界的流+第二次能流通的自由流)。

程式碼

  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 }