3140: [Hnoi2013]消毒
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 679 Solved: 281
[ Submit][ Status]
Description
最近在生物实验室工作的小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中的最小 者。)
Input
Output
仅包含D行,每行一个整数,表示对应实验皿最少要用多少单位 的F试剂。
Sample Input
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
HINT
对于区域(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位。。
专注!!!!!!