给出一个
a
×
b
×
c
≤
5000
a×b×c\leq5000
a×b×c≤5000的立体,其中有一些方块被打上标记,你每次可以选择
x
×
y
×
z
x×y×z
x×y×z的方块,并且消去其中所有打上标记的点,而花费的代价是
m
i
n
{
x
,
y
,
z
}
min\{x,y,z\}
min{x,y,z},求问最少的花费把所有的标记的点全部消掉。
考虑二维平面的问题,一定是分割为若干个
1
×
b
1×b
1×b和
1
×
c
1×c
1×c的条状是最好的,构造一个二分图:左边代表行,右边代表列,对每个点就在行和列之间连边。然后要求的就是一个最小点覆盖,而最小点覆盖等于最大匹配。
再考虑三维的情形,由于
a
×
b
×
c
≤
5000
a×b×c\leq5000
a×b×c≤5000,
m
i
n
{
a
,
b
,
c
}
≤
5000
3
min\{a,b,c\}\leq\sqrt[3]{5000}
min{a,b,c}≤35000,所以只要按照最小的那一维暴力枚举,枚举到的这一面直接花费
1
1
1的代价删除。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=5e3+7;
int x[N],y[N],z[N];
vector<int> G[N];
int a,b,c,tot=0;
bool vis[N];
int used[N],link[N];
int tim=0;
int dfs(int u) {
for(auto &v:G[u]) {
if(used[v]==tim) continue;
used[v]=tim;
if(link[v]==-1||dfs(link[v])) {
link[v]=u;
return 1;
}
}
return 0;
}
int solve(int val) {
tim=0;
for(int i=1;i<=b;i++)
G[i].clear();
for(int i=1;i<=c;i++) {
link[i]=-1;
used[i]=0;
}
int add=0;
for(int i=0;i<a;i++) {
if(val&(1<<i)) vis[i+1]=0,++add;
else vis[i+1]=1;
}
for(int i=1;i<=tot;i++) {
if(vis[x[i]]) {
G[y[i]].push_back(z[i]);
}
}
for(int i=1;i<=b;i++) {
++tim;
if(dfs(i)) ++add;
}
return add;
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
tot=0;
scanf("%d%d%d",&a,&b,&c);
int mi=min(a,min(b,c));
for(int i=1;i<=a;i++) {
for(int j=1;j<=b;j++) {
for(int k=1;k<=c;k++) {
int opt;
scanf("%d",&opt);
if(opt==0) continue;
if(opt==1) {
++tot;
x[tot]=i;
y[tot]=j;
z[tot]=k;
}
}
}
}
if(b==mi) {
swap(a,b);
swap(x,y);
}
else if(c==mi) {
swap(a,c);
swap(x,z);
}
int ans=2e9;
for(int i=0;i<(1<<a);i++)
ans=min(ans,solve(i));
printf("%d\n",ans);
}
return 0;
}