最近在生物实验室工作的小T遇到了大麻烦。
由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为a*b*c,a、b、c 均为正整数。为了实验的方便,它被划分为a*b*c个单位立方体区域,每个单位立方体尺寸
为1*1*1。用(i,j,k)标识一个单位立方体,1 ≤i≤a,1≤j≤b,1≤k≤c。这个实验皿已经很久没有人用了,现在,小T被导师要求将其中一些单位立方体区域进 行消毒操作(每个区域可以被重复消毒)。而由于严格的实验要求,他被要求使用一种特定 的F试剂来进行消毒。 这种F试剂特别奇怪,每次对尺寸为x*y*z的长方体区域(它由x*y*z个单位立方体组 成)进行消毒时,只需要使用min{x,y,z}单位的F试剂。F试剂的价格不菲,这可难倒了小 T。现在请你告诉他,最少要用多少单位的F试剂。(注:min{x,y,z}表示x、y、z中的最小 者。)
仅包含D行,每行一个整数,表示对应实验皿最少要用多少单位 的F试剂。
对于区域(1,1,3)-(2,2,4)和(1,1,1)-(4,4,1)消毒,分别花费2个单位和1个单位的F试剂。
状压+二分图最小点覆盖。
因为使用min(x,y,z)单位的试剂,必然是1*a*b或1*b*c或1*a*c这样切。
注意到a*b*c<=5000,说明三维中最小的那一维的最大值只有17。
然后把最小的那一维当做高,把立方体竖起来。
先假设这一维是a。
那么我们可以用二进制数来枚举每一层是否要消毒,要消毒的把这一层抽出来,累加到答案中。
接下来,俯视这个立方体,把他压扁了,此时就变成一个b*c的二维矩阵了,然后就是二分图最小点覆盖模型了。
用i当做左部点,j当做右部点,(i,j)需要消毒,则连边,求最小点覆盖即最大匹配。
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #define M 5005 using namespace std; int tot=0,times=0,u,v[M],h[M],have[M],cnt,x,y,z; struct read { int i,j,k; }c[M]; int my[M],o,p,q; struct edge { int y,ne; }e[M*10]; int Get1(int x) { int ans=0; while (x) { if (x&1) ans++; x>>=1; } return ans; } void Addedge(int a,int b) { e[++tot].y=b; e[tot].ne=h[a]; h[a]=tot; } bool dfs(int x) { for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (v[y]!=times) { v[y]=times; if (!my[y]||dfs(my[y])) { my[y]=x; return true; } } } return false; } int main() { int T; scanf("%d",&T); while (T--) { cnt=0; for (int i=1;i<=5000;i++) have[i]=0; scanf("%d%d%d",&x,&y,&z); if (x<=y&&x<=z) u=1; else { if (y<=x&&y<=z) u=2; else u=3; } for (int i=1;i<=x;i++) for (int j=1;j<=y;j++) for (int k=1;k<=z;k++) { int r; scanf("%d",&r); if (r) { cnt++; if (u==1) c[cnt].i=i,c[cnt].j=j,c[cnt].k=k; if (u==2) c[cnt].i=j,c[cnt].j=i,c[cnt].k=k; if (u==3) c[cnt].i=k,c[cnt].j=i,c[cnt].k=j; have[c[cnt].i]=1; } } if (u==2) swap(x,y); if (u==3) swap(y,z),swap(y,x); int ans=x; for (int now=0;now<(1<<x);now++) { tot=0; int s=Get1(now); bool f=true; for (int i=0;i<=x;i++) if (((1<<i)&now)&&(!have[i+1])) { f=false; break; } if (!f) continue; for (int i=1;i<=y;i++) h[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 (h[i]) { times++; if (dfs(i)) s++; if (s>=ans) break; } if (s<ans) ans=s; } printf("%d\n",ans); } return 0; }
吼吼~rank5~最近超喜欢优化时间,然后排进第一版~
感悟:
1.我代码中的优化主要有以下几个:
据说读入中1的数量很少,所以把他们都存在数组里;
如果当前答案超过之前最优解,就停止;
不要每次清空v数组,而是用一个变量times,每次++即可。
2.其实这道题思路并不复杂,代码也不复杂,但是调了好长时间,都是细节问题啊:
dfs中的my[y]写成了my[i](以后不能抄模板了。。)
1<<i是第i+1位。。
专注!!!!!!