[HNOI2013]消毒

题目描述

最近在生物实验室工作的小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,表示数据组数。接下来是D组数据,每组数据开头是三个数a,b,c表示实验皿的尺寸。接下来会出现a个b 行c列的用空格隔开的01矩阵,0表示对应的单位立方体不要求消毒,1表示对应的单位立方体需要消毒;例如,如果第1个01矩阵的第2行第3列为1,则表示单位立方体(1,2,3)需要被消毒。输入保证满足a*b*c<=5000,T<=3。

输出格式:

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

输入输出样例

输入样例#1: 
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
输出样例#1: 
3

说明

对于区域(1,1,3)-(2,2,4)和(1,1,1)-(4,4,1)消毒,分别花费2个单位和1个单位的F试剂。

题解:

个人认为这道题很强。。。

题目要求的是三维,那么我们先考虑一个简化版的问题,如果是二维的,我们应该怎么做?

不难发现这个简化版的问题是二分图的裸题:

  1.将行和列分别虚拟成两列点。

  2.将每一个格子虚拟成边连在相对应的行和列上。

  3.然后跑一下二分图就好。

但是如果上升成了三维,我们该怎么办?貌似是不存在三分图的。

观察一下数据范围,我们可以发现a*b*c<=5000,这说明a,b,c中至少有一个小于等于17。

于是我们对这最小的一维的状态进行枚举,即当前层是否被全部消毒。这个过程可以用dfs,我用的是循环枚举。

 1 //Never forget why you start
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<cmath>
 7 #include<algorithm>
 8 #define inf (2000000000)
 9 using namespace std;
10 const int N=5005;
11 int t,cnt,x,y,z;
12 int gi(){
13   int ans=0,f=1;char i=getchar();
14   while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
15   while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
16   return ans*f;
17 }
18 struct point{
19   int x,y,z;
20 }p[N];
21 struct node{
22   int next,to;
23 }edge[N];
24 int head[N],size=0,vis[N],dfscnt,match[N];
25 void putin(int from,int to){
26   size++;
27   edge[size].next=head[from];
28   edge[size].to=to;
29   head[from]=size;
30 }
31 bool dfs(int r){
32   int i;
33   for(i=head[r];i!=-1;i=edge[i].next){
34     int to=edge[i].to;
35     if(vis[to]!=dfscnt){
36       vis[to]=dfscnt;
37       if(!match[to]||dfs(match[to])){
38     match[to]=r;
39     return true;
40       }
41     }
42   }
43   return false;
44 }
45 int solve(int s){
46   int i,ans=0;
47   memset(head,-1,sizeof(head));size=0;
48   memset(match,0,sizeof(match));
49   for(i=1;i<=cnt;i++)if(!((1<<(p[i].x-1))&s))putin(p[i].y,p[i].z);
50   for(i=1;i<=s;i<<=1)if(i&s)ans++;
51   for(i=1;i<=y;i++){
52     ++dfscnt;
53     if(dfs(i))ans++;
54   }
55   return ans;
56 }
57 int main(){
58   int i,j,k,l;
59   t=gi();
60   while(t--){
61     x=gi();y=gi();z=gi();cnt=0;
62     for(i=1;i<=x;i++)
63       for(j=1;j<=y;j++)
64     for(k=1;k<=z;k++){
65       l=gi();
66       if(l){
67         p[++cnt].x=i;
68         p[cnt].y=j;
69         p[cnt].z=k;
70       }
71     }
72     if(z<y&&x>z){
73       swap(x,z);
74       for(i=1;i<=cnt;i++)swap(p[i].x,p[i].z);
75     }
76     if(y<x&&y<z){
77       swap(x,y);
78       for(i=1;i<=cnt;i++)swap(p[i].x,p[i].y);
79     }
80     int tot=(1<<x)-1,ans=inf;
81     for(i=0;i<=tot;i++)ans=min(ans,solve(i));
82     printf("%d\n",ans);
83   }
84   return 0;
85 }

 

posted @ 2018-01-10 21:31  kakakakakaka  阅读(130)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效