题意
最近在生物实验室工作的小 T T T 遇到了 大 麻 烦 大麻烦 大麻烦。
由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为 a × b × c a\times b\times c a×b×c, a a a、 b b b、 c c c 均为正整数。为了实验的方便,它被划分为 a × b × c a\times b\times c a×b×c 个单位立方体区域,每个单位立方体尺寸为 1 × 1 × 1 1\times 1\times 1 1×1×1 。用 ( i , j , k ) (i,j,k) (i,j,k) 标识一个单位立方体, 1 ≤ i ≤ a 1≤i≤a 1≤i≤a, 1 ≤ j ≤ b 1≤j≤b 1≤j≤b, 1 ≤ k ≤ c 1≤k≤c 1≤k≤c 。
这个实验皿已经很久没有人用了,现在,小 T T T 被导师要求将其中一些单位立方体区域进行 消 毒 消毒 消毒操作(每个区域可以被重复消毒)。而由于严格的实验要求,他被要求使用一种特定的 氟 ( F ) 氟(F) 氟(F)试剂来进行消毒。 这种 F F F 试剂特别奇怪,每次对尺寸为 x × y × z x\times y\times z x×y×z 的长方体区域(它由 x × y × z x\times y\times z x×y×z 个单位立方体组成)进行消毒时,只需要使用 m i n { x , y , z } min\{x,y,z\} min{x,y,z} 单位的 F F F 试剂。 F F F 试剂的价格不菲,这可难倒了小 T T T 。现在请你告诉他,最少要用多少单位的 F F F 试剂。(注: m i n { x , y , z } min\{x,y,z\} min{x,y,z} 表示 x x x、 y y y、 z z z 中的最小者)
Input
第一行是一个正整数 D D D ,表示数据组数。接下来是 D D D 组数据,每组数据开头是三个数 a , b , c a,b,c a,b,c 表示实验皿的尺寸。接下来会出现 a a a 个 b b b 行 c c c 列的用空格隔开的 01 01 01 矩阵, 0 0 0 表示对应的单位立方体不要求消毒, 1 1 1 表示对应的单位立方体需要消毒;例如,如果第 1 1 1 个 01 01 01 矩阵的第 2 2 2 行第 3 3 3 列为 1 1 1 ,则表示单位立方体 ( 1 , 2 , 3 ) (1,2,3) (1,2,3) 需要被消毒。输入保证满足 a × b × c ≤ 5000 , T ≤ 3 a\times b\times c≤5000,T≤3 a×b×c≤5000,T≤3。
Output
仅包含 D D D 行,每行一个整数,表示对应实验皿最少要用多少单位的 F F 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
Hint
对于区域 ( 1 , 1 , 3 ) − ( 2 , 2 , 4 ) (1,1,3)-(2,2,4) (1,1,3)−(2,2,4) 和 ( 1 , 1 , 1 ) − ( 4 , 4 , 1 ) (1,1,1)-(4,4,1) (1,1,1)−(4,4,1) 消毒,分别花费 2 2 2 个单位和 1 1 1 个单位的 F F F 试剂。
题解
像这种矩阵、立方体给出面积和体积限制的,许多是可以往最小边长方面想的。
就如这道题, a × b × c ≤ 5000 a\times b\times c\leq 5000 a×b×c≤5000,它便暗示着 a , b , c a,b,c a,b,c 的最小值不超过 17 罢。
另外,只要每次取一个垂直坐标轴的平面喷洒试剂(厚度为 1),便每次都只用花费 1 单位试剂。而且不难发现,最优解的每次喷洒,也可以分成很多个平面的单独喷洒,于是最优解一定就在其中了。
那么吾等大可把这最小的一维单独拎出来,做个最多 2 17 2^{17} 217 的枚举,考虑这一维方向上的最多 17 个平面是否喷洒,然后便将此问题简化为了剩下两维的平面问题了。
我们把剩下未喷洒的平面重起来,变成一个经典的问题:矩阵中选取最少的行或列,覆盖所有的 1 。
将处于 ( x , y ) (x,y) (x,y) 处的 1 看作二分图上连接一条 x → y x\rightarrow y x→y 的边,那么要求的就是该二分图的最小点覆盖了。然而,二分图的最小点覆盖 = 最大匹配,于是用匈牙利算法便解决了。
时间复杂度,稍作思考,便知道它是可过的。
CODE
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<tr1/unordered_map>
using namespace std;
using namespace tr1;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define eps (1e-1)
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
int n,m,s,o,k;
int g[5005][5005];
vector<pair<int,int> > a[20];
int match[5005];
bool v[5005];
bool dfs(int x) {
for(int i = 1;i <= m;i ++) {
if(g[x][i] && !v[i]) {
v[i] = 1;
if(match[i]) {
bool fl = dfs(match[i]);
if(fl) {
match[i] = x;
return 1;
}
}
else {
match[i] = x;
return 1;
}
}
}return 0;
}
int Hungary() {
int res = 0;
for(int j = 1;j <= m;j ++) match[j] = 0;
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) v[j] = 0;
res += dfs(i);
}return res;
}
int main() {
int T = read();
while(T --) {
o = read();n = read();m = read();
int mn = min(n,min(m,o));
for(int i = 1;i <= mn;i ++) a[i].clear();
for(int i = 1;i <= o;i ++) {
for(int j = 1;j <= n;j ++) {
for(int k = 1;k <= m;k ++) {
s = read();
if(!s) continue;
if(o == mn) a[i].push_back(make_pair(j,k));
else if(n == mn) a[j].push_back(make_pair(i,k));
else a[i].push_back(make_pair(i,j));
}
}
}
if(o != mn) {
if(n == mn) swap(o,n);
else swap(o,m),swap(n,m);
}
int tp = (1<<o),ans = mn;
for(int S = 0;S < tp;S ++) {
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
g[i][j] = 0;
}
}
int as = 0;
for(int k = 1;k <= o;k ++) {
if(S & (1<<(k-1))) {
for(int i = 0,le = a[k].size();i < le;i ++) {
g[a[k][i].FI][a[k][i].SE] = 1;
}
}
else as ++;
}
as += Hungary();
ans = min(ans,as);
}
AIput(ans,'\n');
}
return 0;
}