BZOJ2673 [Wf2011]Chips Challenge 费用流 zkw费用流 网络流
https://darkbzoj.cf/problem/2673
有一个芯片,芯片上有N*N(1≤N≤40)个插槽,可以在里面装零件。
有些插槽不能装零件,有些插槽必须装零件,剩下的插槽随意。
要求装好之后满足如下两条要求:
1、第 i 行和第 i 列的零件数目必须一样多(1≤i≤N)。
2、第 i 行的零件数目不能超过总的零件数目的 A/B(1≤i≤N,0≤A≤B≤1000,B≠0)。
求最多可以另外放多少个零件(就是除掉必须放的)。如果无解输出impossible。
zkw费用流就是像跑最大流一样跑费用流,可以并行处理使得稠密图和二分图速度变快(据说)。
棋盘图依然是把横纵坐标拆成2n个点,每一个棋子可以用其横坐标到纵坐标的一条路表示。
棋盘上放最多的棋子相当于所有位置放上棋子后去掉最少的棋子,最小费用流处理就是给每一个可以去掉的棋子一个固定费用(1)。
i行和i列保留零件数目一样多就是从横坐标的i点到纵坐标的i点连一条费用为0流量固定为w的路。 w一定时,每一行每一列保留棋子的数量一定<=w。w上限是n所以我们可以枚举w建n次图跑网络流。
此时的最大流最小费用就是条件w下可以去掉的棋子数量的最小值(不仅要验证满足题目中的要求2,还要验证最大流=所有可以放棋子的位置的数量,因为保留棋子数量有时候可能小于某行列C的数量)(如果这个w跑最大流最小费用的方案不合法,那么就不存在w的合法条件)。
这个建n次图的方法有点像noip2017的那道搜索题目 NOIP2017 D2T2宝藏 都是按层次找的想法。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 using namespace std; 8 #define LL long long 9 const int maxn=60100; 10 int n,A,B; 11 char ch[50][50]; 12 int han[50]={},shu[50]={},sum,num,S,T,mx,cnt; 13 struct nod{ 14 int x,y,next,v,co,rev; 15 }e[maxn]; 16 int head[150]={},tot=0; 17 void init(int x,int y,int v,int co){ 18 e[++tot].y=y;e[tot].x=x;e[tot].v=v;e[tot].co=co;e[tot].next=head[x];e[tot].rev=tot+1;head[x]=tot; 19 e[++tot].y=x;e[tot].x=y;e[tot].v=0;e[tot].co=-co;e[tot].next=head[y];e[tot].rev=tot-1;head[y]=tot; 20 } 21 queue<int>q; 22 int dis[150]={};bool vis[150]={}; 23 bool SPFA(){ 24 memset(dis,63,sizeof(dis)); 25 memset(vis,0,sizeof(vis)); 26 mx=dis[0]; 27 q.push(S);vis[S]=1;dis[S]=0; 28 while(!q.empty()){ 29 int x=q.front(),y;q.pop();vis[x]=0; 30 for(int i=head[x];i;i=e[i].next){ 31 y=e[i].y; 32 if(e[i].v>0&&dis[y]>dis[x]+e[i].co){ 33 dis[y]=dis[x]+e[i].co; 34 if(!vis[y]){ 35 q.push(y);vis[y]=1; 36 } 37 } 38 } 39 } 40 return dis[T]!=mx; 41 } 42 int dfs(int x,int val){ 43 if(x==T){ 44 cnt+=dis[T]*val; 45 return val; 46 } 47 int liu=0,tv,y;vis[x]=1; 48 for(int i=head[x];i;i=e[i].next){ 49 y=e[i].y; 50 if(vis[y])continue; 51 if(e[i].v>0&&dis[y]==dis[x]+e[i].co){ 52 vis[y]=1; 53 tv=dfs(y,min(val-liu,e[i].v)); 54 liu+=tv;e[i].v-=tv;e[e[i].rev].v+=tv; 55 if(liu==val)break; 56 } 57 } 58 return liu; 59 } 60 int main(){ 61 int C=0; 62 while(~scanf("%d%d%d",&n,&A,&B)){ 63 if(n==0&&A==0&&B==0)break; 64 ++C; 65 memset(han,0,sizeof(han));memset(shu,0,sizeof(shu)); 66 sum=0;cnt=0;S=n*2+1;T=S+1; 67 int zz=0,ans=-1; 68 for(int i=0;i<n;i++){ 69 scanf("%s",ch[i]); 70 for(int j=0;j<n;j++){ 71 if(ch[i][j]!='/'){ 72 ++han[i+1];++shu[j+1];++sum; 73 if(ch[i][j]=='C')++zz; 74 } 75 } 76 } 77 for(int w=0;w<=n;w++){ 78 tot=0;memset(head,0,sizeof(head)); 79 for(int i=1;i<=n;i++){ 80 init(S,i,han[i],0); 81 init(i+n,T,shu[i],0); 82 init(i,i+n,w,0); 83 for(int j=1;j<=n;j++){ 84 if(ch[i-1][j-1]=='.')init(i,j+n,1,1); 85 } 86 }int num=0;cnt=0; 87 while(SPFA()){memset(vis,0,sizeof(vis));num+=dfs(S,sum);} 88 if(num==sum&&w*B<=(sum-cnt)*A)ans=max(ans,sum-cnt); 89 }cout<<endl; 90 if(ans==-1)printf("Case %d: impossible\n",C); 91 else printf("Case %d: %d\n",C,ans-zz); 92 } 93 return 0; 94 }