传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3140
思路:首先我们观察题目“只需要使用min{x,y,z}单位的F试剂”
那么我们如果选择一位长度为a,那其他两维直接取到最大即可
那么题目就相当于问最少切多少个面才能覆盖所有点
二维的很简单,直接二分图匹配即可(不会的见poj3041)
http://poj.org/problem?id=3041
三维的怎么办?
这时a*b*c<=5000就有用了
我们2^n枚举最小的一维每层切还是不切(不超过17)
不切的层再二分图匹配即可
复杂度有点坑,但还是可以接受的
千万不要作死用memset
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=5010,inf=1e9,maxm=100010;
using namespace std;
struct poi{int x,y,z;}p[maxn];
int mat[maxn],cnt,cas,pw[20],ans,vis[maxn],pre[maxm],now[maxn],son[maxm],tim,tot,a,b,c;
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
bool cmp(poi a,poi b){
if (a.x!=b.x) return a.x<b.x;
if (a.y!=b.y) return a.y<b.y;
return a.z<b.z;
}
bool dfs(int x){
for (int y=now[x];y;y=pre[y]){
int v=son[y];
if (vis[v]<tim){
vis[v]=tim;
if (!mat[v]||dfs(mat[v])) return mat[v]=x,1;
}
}
return 0;
}
void work(int st){
int res=0,n=b,m=c;tot=0;
for (int i=0;i<a;i++) if (st&pw[i]) res++;
// printf("%d\n",res);
if (res>=ans) return;
for (int i=1;i<=n;i++) now[i]=0;
for (int i=1;i<=m;i++) mat[i]=0;
for (int i=1;i<=cnt;i++) if (!(st&pw[p[i].x-1])) add(p[i].y,p[i].z);
for (int i=1;i<=n;i++){
tim++;
if (dfs(i)) res++;
if (res>=ans) return;
}
ans=res;
}
int main(){
scanf("%d",&cas);
pw[0]=1;for (int i=1;i<=19;i++) pw[i]=pw[i-1]*2;
//for (int i=1;i<=19;i++) printf("pw %d\n",pw[i]);
while (cas--){
scanf("%d%d%d",&a,&b,&c),ans=1e9,cnt=0;
for (int i=1,op;i<=a;i++) for (int j=1;j<=b;j++) for (int k=1;k<=c;k++){scanf("%d",&op);if (op) p[++cnt]=(poi){i,j,k};}
if (b<a){swap(a,b);for (int i=1;i<=cnt;i++) swap(p[i].x,p[i].y);}
if (c<a){swap(a,c);for (int i=1;i<=cnt;i++) swap(p[i].x,p[i].z);}
sort(p+1,p+1+cnt,cmp);
for (int i=0;i<pw[a];i++) work(i);
printf("%d\n",ans);
}
return 0;
}