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;
}

浙公网安备 33010602011771号