传送门: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; }