题目描述 Description
最近在生物实验室工作的小T遇到了大麻烦。
由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为
a∗b∗c
a
∗
b
∗
c
。
a,b,c
a
,
b
,
c
均为正整数。为了实验的方便,它被划分为
a∗b∗c
a
∗
b
∗
c
个单位立方体区域,每个单位立方体尺寸为
1∗1∗1
1
∗
1
∗
1
。用
(i,j,k)
(
i
,
j
,
k
)
标识一个单位立方体,
1≤i≤a,1≤j≤b,1≤k≤c
1
≤
i
≤
a
,
1
≤
j
≤
b
,
1
≤
k
≤
c
。
这个实验皿已经很久没有人用了,现在,小 T 被导师要求将其中一些单位立方体区域进行消毒操作(每个区域可以被重复消毒)。而由于严格的实验要求,他被要求使用一种特定的F试剂来进行消毒。
这种F试剂特别奇怪,每次对尺寸为
x∗y∗z
x
∗
y
∗
z
的长方体区域(它由
x∗y∗z
x
∗
y
∗
z
个单位立方体组成)进行消毒时,只需要使用
minx,y,z
m
i
n
x
,
y
,
z
单位的F试剂。F试剂的价格不菲,这可难倒了小T。现在请你告诉他,最少要用多少单位的F试剂。(注:
minx,y,z
m
i
n
x
,
y
,
z
表示
x,y,z
x
,
y
,
z
中的最小者。)
输入描述 Input Description
第一行是一个正整数
D
D
,表示数据组数。
接下来是组数据,每组数据开头是三个数
a,b,c
a
,
b
,
c
表示实验皿的尺寸。接下来会出现
a
a
个行
c
c
列的用空格隔开的01矩阵,0表示对应的单位立方体不要求消毒,1表示对应的单位立方体需要消毒;例如,如果第1个01矩阵的第2行第3列为1,则表示单位立方体(1,2,3)需要被消毒。
输出描述 Output Description
仅包含行,每行一个整数,表示对应实验皿最少要用多少单位的F试剂。
样例输入 Sample Input
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
样例输出 Sample Output
3
样例解释
对于区域(1,1,3)-(2,2,4)和(1,1,1)-(4,4,1)消毒,分别花费2个单位和1个单位的F试剂。
数据范围及提示 Data Size & Hint
输入保证满足
a∗b∗c≤5000,T≤3
a
∗
b
∗
c
≤
5000
,
T
≤
3
。
Solution
强烈安利二维简单版
[POJ3041]Asteroids
首先考虑二维的情况
显然每次覆盖都是选择横向的一排或者纵向的一列才是最优的
那么每个点会被一个横向的一排覆盖或者纵向的一列覆盖(或者两者皆有?)
那么,转化为二分图最小点覆盖问题,将横坐标作为一侧的节点,将纵坐标作为另外一侧的节点,对于每一个点
(x,y)
(
x
,
y
)
,将左侧的x节点和右侧y节点连接起来.这样,问题就变为了怎么让每一条边两端至少有一边被覆盖,即二分图最小点覆盖问题
emm,如果你还记得的话,最小点覆盖就是二分图的最大匹配数,不会的话记得复习二分图&网络流哦
所以直接跑匈牙利就好了
那么三维呢?
首先,可以看到数据范围中
a∗b∗c≤5000
a
∗
b
∗
c
≤
5000
,显然
min(a,b,c)≤5000−−−−√3≤17
m
i
n
(
a
,
b
,
c
)
≤
5000
3
≤
17
那么可以枚举最小的一维然后强行将其转化为二维情况
枚举最小的一维中0为直接将这一层全部消毒,1为这一层暂时不消毒
然后把剩下的那些没有消毒的层放在一起,全部压到一张图上,二分图匹配即可
代码如下
#include <bits/stdc++.h>
using namespace std;
#define N 5005
#define lowbit(x) ((x)&(-(x)))
struct edge {
int to;
int next;
}e[N*10];
struct C {
int i;
int j;
int k;
}c[N];
int head[N],tot;
int my[N],use[N],have[N];
int x,y,z,s,cnt,times;
int read() {
int ans=0,flag=1;
char ch=getchar();
while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
int get(int x) {
int ans=0;
while(x) {
x-=lowbit(x);
ans++;
}
return ans;
}
void addedge(int u,int v) {
e[++tot].to=v;
e[tot].next=head[u];
head[u]=tot;
}
bool dfs(int x) {
for(int i=head[x];i;i=e[i].next) {
int to=e[i].to;
if(use[to]!=times) {
use[to]=times;
if(!my[to] || dfs(my[to])) {
my[to]=x;
return true;
}
}
}
return false;
}
int main() {
int T=read();
while(T--) {
cnt=0;
x=read();y=read();z=read();
for(int i=1;i<=x*y*z;i++) have[i]=0;
if(x<=y && x<=z) s=1;
else if(y<=x && y<=z) s=2;
else s=3;
for(int i=1;i<=x;i++)
for(int j=1;j<=y;j++)
for(int k=1;k<=z;k++) {
int r=read();
if(r) {
cnt++;
if(s==1) {c[cnt].i=i;c[cnt].j=j;c[cnt].k=k;}
else if(s==2) {c[cnt].i=j;c[cnt].j=i;c[cnt].k=k;}
else {c[cnt].i=k;c[cnt].j=i;c[cnt].k=j;}
have[c[cnt].i]=1;
}
}
if(s==2) {swap(x,y);}
if(s==3) {swap(x,y);swap(y,z);}
int ans=x;
for(int now=0;now<(1<<x);now++) {
tot=0;
int num=get(now);
bool flag=0;
for(int i=0;i<x;i++) {
if(((1<<i)&now) && (!have[i+1])) {
flag=1;
break;
}
}
if(flag) continue;
for(int i=1;i<=y;i++) head[i]=0;
for(int i=1;i<=cnt;i++) {
if(1<<(c[i].i-1)&now) continue;
addedge(c[i].j,c[i].k);
}
for(int i=1;i<=z;i++) my[i]=0;
for(int i=1;i<=y;i++) {
if(head[i]) {
times++;
if(dfs(i)) num++;
if(num>=ans) break;
}
}
if(num<ans) ans=num;
}
printf("%d\n",ans);
}
return 0;
}