P3231 [HNOI2013] 消毒

P3231 [HNOI2013] 消毒

题目描述

最近在生物实验室工作的小 T 遇到了大麻烦。 由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为 \(a\times b\times c\)。为了实验的方便,它被划分为 \(a\times b\times c\) 个单位立方体区域,每个单位立方体尺寸为 \(1\times 1\times 1\),并用 \((i,j,k)\) 标识一个单位立方体。这个实验皿已经很久没有人用了。现在,小 T 被导师要求将其中一些单位立方体区域进行消毒操作(每个区域可以被重复消毒)。

而由于严格的实验要求,他被要求使用一种特定的 F 试剂来进行消毒。 这种 F 试剂特别奇怪,每次对尺寸为 \(x\times y\times z\) 的长方体区域(它由 \(x\times y\times z\) 个单位立方体组成)进行消毒时,只需要使用 \(\min(x,y,z)\) 单位的 F 试剂。F 试剂的价格不菲,这可难倒了小 T。

现在请你告诉他,最少要用多少单位的 F 试剂。

输入格式

本题有多组数据。

第一行是一个正整数 \(D\),表示数据组数。

接下来是 \(D\) 组数据,每组数据第一行是三个正整数 \(a,b,c\) 表示实验皿的尺寸。

接下来会出现 \(a\)\(b\)\(c\) 列的用空格隔开的 01 矩阵,0 表示对应的单位立方体不要求消毒,1 表示对应的单位立方体需要消毒:如,如果第 \(1\)01 矩阵的第 \(2\) 行第 \(3\) 列为 1,则表示单位立方体 \((1,2,3)\) 需要被消毒。

输出格式

\(D\) 行,每行一个整数,表示对应实验皿最少要用多少单位的 F 试剂。

输入输出样例 #1

输入 #1

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

输出 #1

3

说明/提示

样例 1 解释

对于区域 \((1,1,3)-(2,2,4)\)\((1,1,1)-(4,4,1)\) 消毒,分别花费 \(2\) 个单位和 \(1\) 个单位的 F 试剂。

数据规模与约定

对于 \(100\%\) 的数据,保证 \(1\le a,b,c\le 5\times 10^3\)\(abc\le 5\times 10^3\),且 \(1\le D\le 3\)

Solution:

我们先看数据范围,发现 \(1\le a,b,c\le 5\times 10^3\)\(abc\le 5\times 10^3\) 然后我们拿计算器算一下就知道,\(17< \sqrt[3]{5 \times 10^3} <18\). 也就是说,\(\min(a,b,c) \le 17\) 所以我们将 \(a,b,c\) 从小到大排序,然后重建坐标,这样我们就能得到 \(x\le17\)

然后我们不难想到,我们每次最优的消毒的长方体都是形如 \((1,y,z) , (x,1,z) , (x,y,1)\) 这样的。所以我们可以对 \(x\) 个二维平面跑一个搜索,每个二进制位表示是否将 \(x=k\) 这个维位平面消毒,然后我们将没有消毒的那些二维平面上的点选出来,对他们按照 (y,z) 的坐标拍到一个二维平面上,在这个平面上,问题就转化为了:

在二维平面上有 \(n\) 个点 \((y,z)\) ,每次选一行或者一列将这行或者这列上的点全部消除,求最少消除次数。

非常经典的网络流题目对吧,我们将行列分开,二分图左边的点代表行,右边的点代表列,然后对于每个点 \((y,z)\) ,将第 \(y\) 行与第 \(z\) 列连一条边,那么二分图上的最大匹配 \(match\) 就是最小消除次数 \(sum\)

然后答案就是所有枚举的状态中 \(sum\) 的最小值。

Code:

#include<bits/stdc++.h>
const int N=10005;
using namespace std;
int head[N],vis[N],match[N],in[N];
int q[4][N];
int a,b,c,e_cnt,tim,tot,ans=1e9;
struct Edge{
    int to,nxt;
}e[N<<2];
void add(int x,int y)
{
    e[++e_cnt]={y,head[x]};
    head[x]=e_cnt;
}
void init()
{
    e_cnt=tim=0;
    for(int i=1;i<=max(b,c);i++)
    {
        head[i]=vis[i]=match[i]=0;
    }
}
bool dfs(int x)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]==tim)continue;
        vis[to]=tim;
        if(!match[to]||dfs(match[to]))
        {
            match[to]=x;
            return 1;
        }
    }
    return 0;
}
void solve(int x)
{
    init();
    int zt=x,sum=0;
    for(int i=1;i<=a;i++)
    {
        if(zt&1)
        {
            in[i]=0;
            sum++;
        }
        else in[i]=1;
        zt>>=1;
    }
    for(int i=1;i<=tot;i++)
    {
        if(in[q[1][i]])
        {
            add(q[2][i],q[3][i]);
        }
    }
    for(int i=1;i<=b;i++)
    {
        tim++;
        sum+=dfs(i);
    }
    ans=min(ans,sum);
}
void work()
{
    tot=0;
    ans=1e9;
    cin>>a>>b>>c;
    for(int i=1,opt;i<=a;i++)
    {
        for(int j=1;j<=b;j++)
        {
            for(int k=1;k<=c;k++)
            {
                std::cin>>opt;
                if(opt)
                {
                    tot++;
                    q[1][tot]=i;q[2][tot]=j;q[3][tot]=k;
                }
            }
        }
    }
    if(a>b)
    {
        swap(a,b);
        swap(q[1],q[2]);
    }
    if(a>c)
    {
        swap(a,c);
        swap(q[1],q[3]);
    }
    int idx=(1<<a)-1;
    for(int zt=0;zt<=idx;zt++)
    {
        solve(zt);
    }
    printf("%d\n",ans);
}
int main()
{
    //freopen("disinfect.in","r",stdin);
    //freopen("disinfect.out","w",stdout);
    int T;
    cin>>T;
    while(T--)work();
    return 0;
}

posted @ 2025-06-28 17:22  liuboom  阅读(11)  评论(0)    收藏  举报