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 }
View Code

 

posted @ 2018-05-02 21:56  鲸头鹳  阅读(392)  评论(0编辑  收藏  举报