1. 程式人生 > >解題:BZOJ 2673 World Final 2011 Chips Challenge

解題:BZOJ 2673 World Final 2011 Chips Challenge

題面

資料範圍看起來很像網路流誒(滾那

因為限制多而且強,資料範圍也不大,我們考慮不直接求答案,而是轉化為判定問題

可以發現第二個限制相對好滿足,我們直接列舉這個限制就可以。具體來說是列舉所有行中的最大值$x$,然後下面那個式子移項就可以得到$a*tot>=b*x$,其中tot表示晶片的總數

然後發現第一個限制還是很強,不好滿足。怎麼辦呢?正難則反,轉化成補集的問題:先把所有能安的晶片都安了,然後扣出來合法的答案

那麼現在我們要扣掉的晶片儘量少,同時還要先保證扣出來的結果合法,那考慮用最小費用最大流,用最大流的限制使得答案合法,用最小費用求答案

具體來說我們這樣建圖(下面再解釋):

①把原圖拆成行和列共n個點

②記錄每行每列最多的晶片個數(包括已經有的和空位),從原點向一邊連邊,另一邊向匯點連邊(這裡為了方便我預設原點向行連,匯點向列連)。容量為晶片個數,費用為零(這個不用解釋吧)

③第i行向第i列連容量為我們列舉的答案的邊,表示至多留下這些晶片,費用仍然為零

④對於每個空位(x,y),從第x行向第y列連流量為1費用為1的邊,表示同時從這一行一列扣掉一個晶片

然後跑最小費用最大流,當且僅當最大流等於晶片的最大總數(包括已經有的和空位))時更新答案,為什麼?

因為最小費用最大流會優先保證滿流,所以我們③中連的邊相當於限制了每行每列的晶片個數一樣,就滿足了第一個限制

大概就是這樣,我覺得這題還是挺有意思的=。=

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int B=50,N=4005,M=100005,inf=1e9;
 7 int mxr[B],mxc[B],mapp[B][B]; char rd[B]; 
 8 int mflw[N],mcst[N],pren[N],pree[N],queu[N]; 
 9
int p[N],noww[2*M],goal[2*M],flow[2*M],cost[2*M]; 10 int n,a,b,s,t,id,ocp,tot,num,cnt,ans,maxf,minc; queue<int> qs; 11 void Link(int f,int t,int v,int c) 12 { 13 noww[++cnt]=p[f],p[f]=cnt; 14 goal[cnt]=t,flow[cnt]=v,cost[cnt]=c; 15 noww[++cnt]=p[t],p[t]=cnt; 16 goal[cnt]=f,flow[cnt]=0,cost[cnt]=-c; 17 } 18 void Setit() 19 { 20 memset(mxr,0,sizeof mxr); 21 memset(mxc,0,sizeof mxc); 22 ocp=tot=0,ans=-1,s=n*2+1,t=s+1; 23 for(int i=1;i<=n;i++) 24 { 25 scanf("%s",rd+1); 26 for(int j=1;j<=n;j++) 27 { 28 mapp[i][j]=rd[j],ocp+=rd[j]=='C'; 29 if(rd[j]!='/') mxr[i]++,mxc[j]++,tot++; 30 } 31 } 32 } 33 void Init(int st,int ed) 34 { 35 memset(mflw,0x3f,sizeof mflw); 36 memset(mcst,0x3f,sizeof mcst); 37 memset(queu,0,sizeof queu),pren[ed]=-1; 38 qs.push(st),queu[st]=true,mcst[st]=0; 39 } 40 bool SP(int st,int ed) 41 { 42 Init(st,ed); 43 while(!qs.empty()) 44 { 45 int tn=qs.front(); 46 qs.pop(),queu[tn]=false; 47 for(int i=p[tn],g;i;i=noww[i]) 48 if(mcst[g=goal[i]]>mcst[tn]+cost[i]&&flow[i]) 49 { 50 pree[g]=i,pren[g]=tn; 51 mcst[g]=mcst[tn]+cost[i]; 52 mflw[g]=min(mflw[tn],flow[i]); 53 if(!queu[g]) qs.push(g),queu[g]=true; 54 } 55 } 56 return ~pren[ed]; 57 } 58 void MCMF(int st,int ed) 59 { 60 while(SP(st,ed)) 61 { 62 maxf+=mflw[ed],id=ed; 63 minc+=mflw[ed]*mcst[ed]; 64 while(id!=st) 65 { 66 flow[pree[id]]-=mflw[ed]; 67 flow[pree[id]^1]+=mflw[ed]; 68 id=pren[id]; 69 } 70 } 71 } 72 int Solve(int x) 73 { 74 memset(p,0,sizeof p),cnt=1,maxf=minc=0; 75 for(int i=1;i<=n;i++) 76 Link(s,i,mxr[i],0),Link(i+n,t,mxc[i],0),Link(i,i+n,x,0); 77 for(int i=1;i<=n;i++) 78 for(int j=1;j<=n;j++) 79 if(mapp[i][j]=='.') Link(i,j+n,1,1); MCMF(s,t); 80 return maxf==tot?(a*(tot-minc)>=b*x?tot-ocp-minc:-1):-1; 81 } 82 int main() 83 { 84 while(scanf("%d%d%d",&n,&a,&b)!=EOF) 85 if(n||a||b) 86 { 87 Setit(); 88 for(int i=0;i<=n;i++) 89 ans=max(ans,Solve(i)); 90 printf("Case %d: ",++num); 91 ~ans?printf("%d\n",ans):printf("impossible\n"); 92 } 93 else break; 94 return 0; 95 }
View Code