解题: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

 

转载于:https://www.cnblogs.com/ydnhaha/p/10236892.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值