[bzoj3140][HNOI2013]消毒

题目大意

有a*b*c的立方体,有些位置需要染色。可重复染色,可染不需染色的位置。染x*y*z得立方体需要min(x,y,z)的费用。求最小费用,使所有需要被染色的位置被染色。a*b*c<=5000。

转换

我们发现,最优策略每次只会染1*b*c或a*1*c或a*b*1。
因此转换为三维最小集覆盖问题。
二维可以利用二分图的最小覆盖来做,那三维怎么办?

深搜

因为a*b*c<=5000
所以min(a,b,c)<=17。
我们用2^17枚举最小那一维,然后对于剩下的用二分图最小覆盖解决即可。
注意加最小答案优化。

参考程序

#include<cstdio>
#include<vector>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
vector<int> hx[5000+10],hy[5000+10],hz[5000+10];
struct dong{
    int x,y,z;
};
dong d[5000+10];
int i,j,k,l,t,n,m,ans,ca,a,b,c,top,czy,xdl;
bool p[5000+10][5000+10],pd[5000*3+10];
int r[5000*3+10],bz[5000+10];
void add(int x,int y,int z){
    if (czy==1){
        d[++top].x=x;d[top].y=y;d[top].z=z;
        hx[x].push_back(top);hy[y].push_back(top);hz[z].push_back(top);
    }
    else if (czy==2){
        d[++top].x=y;d[top].y=x;d[top].z=z;
        hx[y].push_back(top);hy[x].push_back(top);hz[z].push_back(top);
    }
    else{
        d[++top].x=z;d[top].y=x;d[top].z=y;
        hx[z].push_back(top);hy[x].push_back(top);hz[y].push_back(top);
    }
}
bool hungary(int x){
    int i;
    fo(i,c+1,c+b)
        if (p[x][i]&&!pd[i]){
            pd[i]=1;
            if (!r[i]||hungary(r[i])){
                r[i]=x;
                return 1;
            }
        }
    return 0;
}
void dfs(int x,int v){
    if (v>=ans) return;
    if (x>xdl){
        int i,j;
        fo(i,1,b)
            fo(j,1,c)
                p[i][j+b]=0;
        fo(i,1,top)
            if (!bz[i])
                p[d[i].y][d[i].z+b]=1;
        fo(i,1,b+c) r[i]=0;
        int tmp=0;
        fo(i,1,b){
            fo(j,1,b+c) pd[j]=0;
            if (hungary(i)) tmp++;
            if (v+tmp>=ans) return;
        }
        ans=v+tmp;
        return;
    }
    vector<int>::iterator it=hx[x].begin(),gjx=hx[x].end();
    while (it!=gjx){
        bz[*it]++;
        it++;
    }
    dfs(x+1,v+1);
    it=hx[x].begin();
    while (it!=gjx){
        bz[*it]--;
        it++;
    }   
    dfs(x+1,v);
}
int main(){
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d%d",&a,&b,&c);
        if (a<=b&&a<=c) czy=1;
        else if (b<=a&&b<=c) czy=2;
        else czy=3;
        if (czy==1) ans=a;
        else if (czy==2) ans=b;
        else ans=c;
        top=0;
        fo(i,1,a)
            fo(j,1,b)
                fo(k,1,c){
                    scanf("%d",&t);
                    if (t==1) add(i,j,k);
                }
        if (czy==2) swap(a,b);
        else if (czy==3){
            swap(a,c);
            swap(b,c);
        }
        xdl=ans;
        dfs(1,0);
        printf("%d\n",ans);
        fo(i,1,a) hx[i].clear();
        fo(i,1,b) hy[i].clear();
        fo(i,1,c) hz[i].clear();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值