bzoj3140 hnoi2013 消毒(暴力出奇迹)

【问题】
最近在生物实验室工作的小T遇到了大麻烦。
由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为a*b*c,a、b、c 均为正整数。为了实验的方便,它被划分为a*b*c个单位立方体区域,每个单位立方体尺寸为1*1*1。用(i,j,k)标识一个单位立方体(1≤i≤a,1≤j≤b,1≤k≤c)。
这个实验皿已经很久没有人用了,现在,小T被导师要求将其中一些单位立方体区域进 行消毒操作(每个区域可以被重复消毒)。而由于严格的实验要求,他被要求使用一种特定 的F试剂来进行消毒。
这种F试剂特别奇怪,每次对尺寸为x*y*z的长方体区域(它由x*y*z个单位立方体组 成)进行消毒时,只需要使用min{x,y,z}单位的F试剂。F试剂的价格不菲,这可难倒了小 T。现在请你告诉他,最少要用多少单位的F试剂。(注:min{x,y,z}表示x、y、z中的最小 者。)
【输入】
第一行是一个正整数D,表示数据组数。接下来是D组数据,每组数据开头是三个数a,b,c表示实验皿的尺寸。接下来会出现a个b 行c列的用空格隔开的01矩阵,0表示对应的单位立方体不要求消毒,1表示对应的单位立方体需要消毒;例如,如果第1个01矩阵的第2行第3列为1,则表示单位立方体(1,2,3)需要被消毒。
【输出】
仅包含D行,每行一个整数,表示对应实验皿最少要用多少单位 的F试剂。
【样例】
C.in C.out
1
4 4 4
1 0 1 1
0 0 1 1
0 0 0 0
0 0 0 0
0 0 1 1
1 0 1 1
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
1 0 0 0 3
【解释】
对于区域(1,1,3)-(2,2,4)0和(1,1,1)-(4,4,1)消毒,分别花费2个单位和1个单位的F试剂。
【限定】
输入保证满足a*b*c≤5000,T≤3。

bzoj3140 hnoi2013

不知道今天怎么了,看什么都能暴力出奇迹,事实证明我赢了。

我们只看花费为1的方案,发现有3种,而其他方案不过是若干个这种单位方案的组合。

想到这里不难看出每个点有3种方法可以消灭它,所以问题就变成了选若干种方案来消灭所有点。

由于答案不会超过min(x,y,z)所以也就不可能过20,这样我们可以直接枚举每个方案选还是不选,加一些优化答案也就能跑出来了。

有可能会爆2组但真正考试的时候,我们将会有更多时间做其他题,这20分实际上可以放弃。确实要做,也能拿这个当暴力。

代码如下:(注意优化的地方)

#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5005;

struct shu
{
    int v[maxn],tot;
    friend bool operator <(shu a,shu b)
    {
        return a.tot>b.tot;
    }
}a[maxn];
int num,ans,vis[maxn],x,y,z;

void init(){
    int id;
    num=0;
    scanf("%d%d%d",&x,&y,&z);
    ans=min(x,min(y,z));
    for(int i=1;i<=x+y+z;i++)
    a[i].tot=0;
    for(int i=1;i<=x;i++)
    for(int j=1;j<=y;j++)
    for(int k=1;k<=z;k++)
    {
        scanf("%d",&id);
        if(id)
        {
            num++;
            a[i].v[++a[i].tot]=num;
            a[x+j].v[++a[x+j].tot]=num;
            a[x+y+k].v[++a[x+y+k].tot]=num;
        }
    }
}

void run(int i,int t,int tt)
{
    if(t>=ans) return;
    if(tt==num) {ans=t;return;}
    if(i>x+y+z) return;
    if(a[i].tot*(ans-t)<=num-tt) return;
    int ok=0;
    for(int k=1;k<=a[i].tot;k++)if(!vis[a[i].v[k]])
    {
        ok++;
        vis[a[i].v[k]]=i;
    }
    if(ok>0)
    {
        run(i+1,t+1,tt+ok);
        for(int k=1;k<=a[i].tot;k++)if(vis[a[i].v[k]]==i)
        vis[a[i].v[k]]=0;
    }
    run(i+1,t,tt);
}
int main()
{
    //freopen("C.in","r",stdin);
    //freopen("C.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        if(num==x*y*z) printf("%d\n",ans);
        else
        {
        sort(a+1,a+x+y+z+1);
        memset(vis,0,sizeof(vis));
        run(1,0,0);
        printf("%d\n",ans);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值