【BZOJ 3140】 [Hnoi2013]消毒

3140: [Hnoi2013]消毒

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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

第一行是一个正整数D,表示数据组数。接下来是D组数据,每组数据开头是三个数a,b,c表示实验皿的尺寸。接下来会出现a个b 行c列的用空格隔开的01矩阵,0表示对应的单位立方体不要求消毒,1表示对应的单位立方体需要消毒;例如,如果第1个01矩阵的第2行第3列为1,则表示单位立方体(1,2,3)需要被消毒。输入保证满足a*b*c≤5000,T≤3。

Output

仅包含D行,每行一个整数,表示对应实验皿最少要用多少单位 的F试剂。

Sample Input

1
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

3

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位。。


专注!!!!!!

你可能感兴趣的:(二分图,OI,bzoj)