[bzoj3140][Hnoi2013]消毒——枚举+最大二分图匹配 大佬们的博客 Some Links

题目大意:

一个长方体中有一些格子需要消毒,消毒一个 a b c 的格子需要的费用为 min ( a , b , c ) 问将所有的格子都消毒的最下费用为多少。

思路:

先来考虑二维下怎么消毒:先确定计算费用的那一维度,然后剩下的一维就拓展到低,所以最后的形式必定是 x b 或者 a x 。对于 x b 或者 a x 这种形式,我们把它拆开,分为 x 1 b 或者 x a 1 ,然后用这些长条当做点取覆盖用格子构成的边,这样跑最小点覆盖,答案一定是最优且符合条件的。我一开始想 x b x > b 的时候我们还是用 x 个条形去覆盖的话那岂不是错了,发现这种情况下跑最小点覆盖的话用b个 a 1 反而会更优,即最小点覆盖完美地避免了这种情况。
那么三维的也就是类似了,大概就是三种点 a b 1 , a 1 c , 1 b c ,对于每一条边 ( x , y , z ) 它要将三个点互相连在一起,然后只要有一个点选了这条边就算覆盖了,然后求最小点覆盖。发现不会做这种清奇的模型,发现最小的一维只有17,可以 2 17 暴力枚举最小的那一维选择那些点然后剩下的两维对于空间中未被前一维覆盖的点跑最小点覆盖就好了。
注意代码的优化,在枚举的时候不要重新建图,建图的时候状压下每一条边(边所代表的两个面的交集就是一维的)中间所要消毒的部分,然后在枚举的时候判断一下子集就好了。
要卡常。。。

/*=================================
 * Author : ylsoi
 * Problem : bzoj3140
 * Algrithm : Max Node Covering
 * Time : 2018.6.16
 * ==============================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<ctime>
using namespace std;
void File(){
    freopen("bzoj3140.in","r",stdin);
    freopen("bzoj3140.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
#define A(x) ((int)ceil(x*1.0/(b*c)))
#define B(x) ((int)ceil(((x-1)%(b*c)+1)*1.0/b))
#define C(x) (((x-1)%(b*c))%b+1)
#define F(aa,bb,cc) ((aa-1)*b*c+(bb-1)*b+cc)
const int maxn=5000+10;
int D,a,b,c,beg[maxn],cnte,num[maxn][maxn],ans=inf;
bool mp[maxn];
struct edge{int to,last;}E[maxn*2];
void add(int u,int v){
    ++cnte;
    E[cnte].to=v;
    E[cnte].last=beg[u];
    beg[u]=cnte;
}
void init(){
    scanf("%d%d%d",&a,&b,&c);
    int len=a*b*c;
    REP(i,1,len)cin>>mp[i];
    if(a<=b && a<=c){
        REP(j,1,b)REP(k,1,c){
            REP(i,1,a)if(mp[F(i,j,k)])
                num[j][k]|=1<<(i-1);
            if(num[j][k])add(j,k);
        }
    }
    else if(b<=a && b<=c){
        REP(i,1,a)REP(k,1,c){
            REP(j,1,b)if(mp[F(i,j,k)])
                num[i][k]|=1<<(j-1);
            if(num[i][k])add(i,k);
        }
    }
    else if(c<=a && c<=b){
        REP(i,1,a)REP(j,1,b){
            REP(k,1,c)if(mp[F(i,j,k)])
                num[i][j]|=1<<(k-1);
            if(num[i][j])add(i,j);
        }
    }
}
int be[maxn],cnt_vis,vis[maxn],S0,cnt_be,bb[maxn];
int Hungary(int u){
    MREP(i,u){
        int v=E[i].to;
        if((num[u][v]|S0)==S0)continue;
        if(vis[v]==cnt_vis)continue;
        vis[v]=cnt_vis;
        if(bb[v]!=cnt_be || Hungary(be[v])){    
            be[v]=u;
            bb[v]=cnt_be;
            return true;
        }
    }
    return false;
}
int cal(int t){
    int ret=0;
    ++cnt_be;
    DREP(i,t,1){
        ++cnt_vis;
        ret+=Hungary(i);
    }
    return ret;
}
void work(){
    if(a<=b && a<=c){
        for(S0=(1<<a)-1;S0>=0;--S0)
            chkmin(ans,__builtin_popcount(S0)+cal(b));
    }
    else if(b<=a && b<=c){
        for(S0=(1<<b)-1;S0>=0;--S0)
            chkmin(ans,__builtin_popcount(S0)+cal(a));
    }
    else if(c<=a && c<=b){
        for(S0=(1<<c)-1;S0>=0;--S0)
            chkmin(ans,__builtin_popcount(S0)+cal(a));
    }
}
int main(){
    File();
    scanf("%d",&D);
    while(D--){
        ans=inf;
        mem(beg);
        mem(num);
        cnte=0;
        init();
        work();
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80711182
今日推荐