[Wf2011]Chips Challenge

两个条件都不太好处理

每行放置的个数实际很小,枚举最多放x

但还是不好放

考虑所有位置先都放上,然后删除最少使得合法

为了凑所有的位置都考虑到,把它当最大流

但是删除最少,所以最小费用

行列相关,左行点,右列点

S到行,流“能填位置”费0

列到T,流“能填位置”费0

i行到i列,流x,即枚举的最大个数

空位(i,j),i行连j列,流1费0

最小费用最大流

意义:流过i行到i列的流量,象征留下一个芯片

流过费用为1的,象征把这个芯片删除。

最大流保证所有位置都考虑到了

最小费用使得最少。

可以发现最后的结果一定满足条件1

 

条件2?

最大流为flow,费用为cos,总共的位置(多出来的+必填)=sum

放置了tot=sum-cos

如果有x/tot<=A/B那么更新ans=max(ans,tot)
x一定时,tot越大,越可能比A/B小。和最小费用相符。

虽然可能x过大,但是答案一定可以枚举到。

 

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=42;
const int P=86;
const int inf=0x3f3f3f3f;
int n,A,B;
struct node{
    int nxt,to;
    int c,w;
}e[2*(N*N+2*N)+233];
int hd[P],cnt=1;
int s,t;
void add(int x,int y,int w,int c){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].c=c;
    e[cnt].w=w;hd[x]=cnt;
    
    e[++cnt].nxt=hd[y];
    e[cnt].to=x;
    e[cnt].c=-c;
    e[cnt].w=0;hd[y]=cnt;
}
int dis[P];
bool in[P];
int incf[P],pre[P];
queue<int>q;
int ans,flow,cos;
int l[N],h[N];
char mp[N][N];
bool spfa(){
    memset(dis,inf,sizeof dis);
    memset(pre,0,sizeof pre);
    while(!q.empty()) q.pop();
    dis[s]=0;
    incf[s]=inf;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        in[x]=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(e[i].w){
                if(dis[y]>dis[x]+e[i].c){
                    dis[y]=dis[x]+e[i].c;
                    pre[y]=i;
                    incf[y]=min(incf[x],e[i].w);
                    if(!in[y]){
                        in[y]=1;
                        q.push(y);
                    }
                }
            }
        }
    }
    if(dis[t]==inf) return false;
    return true;
}
void upda(){
    int x=t;
    while(pre[x]){
        e[pre[x]].w-=incf[t];
        e[pre[x]^1].w+=incf[t];
        x=e[pre[x]^1].to;
    }
    flow+=incf[t];
    cos+=dis[t]*incf[t];
}
void EK(int lim){
    cos=0;flow=0;
    memset(hd,0,sizeof hd);
    cnt=1;
    s=0,t=2*n+1;
    for(reg i=1;i<=n;++i) {
        add(s,i,l[i],0);
        add(i+n,t,h[i],0);
        add(i,i+n,lim,0);
    }
    for(reg i=1;i<=n;++i){
        for(reg j=1;j<=n;++j){
            if(mp[i][j]=='.') add(i,j+n,1,1);
        }
    }
    while(spfa()) upda();
}
void clear(){
    ans=-2333;
    for(reg i=1;i<=n;++i) l[i]=n;
    for(reg j=1;j<=n;++j) h[j]=n;
}
int main(){
    int o=0;
    while(1){
        rd(n);rd(A);rd(B);
        if(n==0&&A==0&&B==0) break;
        ++o;
        clear();
        int can=0;
        int alr=0;
        for(reg i=1;i<=n;++i){
            scanf("%s",mp[i]+1);
            for(reg j=1;j<=n;++j){
                if(mp[i][j]=='/') --l[i],--h[j];
                else ++can;
                if(mp[i][j]=='C') ++alr;
            }
        }
        for(reg x=0;x<=n;++x){
            EK(x);
            int tot=can-cos;
            if(flow==can&&A*tot>=x*B) ans=max(ans,tot);
        }
        ans-=alr;
        printf("Case %d: ",o);
        if(ans<0) printf("impossible");
        else printf("%d",ans);
        puts("");
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/1/8 10:23:22
*/
View Code

最小费用最大流可以考虑两个限制

最大流限制合法

费用流限制优秀

posted @ 2019-01-08 17:21  *Miracle*  阅读(522)  评论(0编辑  收藏  举报