[HNOI2013]消毒 (匈牙利最大匹配)

这篇博客探讨了一种三维空间中的消毒问题,其中涉及到一个长方体实验皿的消毒策略。实验皿被划分为多个单位立方体,每个需要消毒的单位立方体被标记为1。问题转化为在有限的资源下,如何使用最少的F试剂进行消毒,其中F试剂的消耗与消毒区域的最小边长相等。博主提出了从二维最大匹配问题扩展到三维的思路,通过状压和二维最大匹配算法来解决。博客给出了样例输入和输出,并展示了代码实现。
摘要由CSDN通过智能技术生成

Description

最近在生物实验室工作的小T遇到了大麻烦。
由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为abc,a、b、c 均为正整数。为了实验的方便,它被划分为abc个单位立方体区域,每个单位立方体尺寸为111。用(i,j,k)标识一个单位立方体,1 ≤i≤a,1≤j≤b,1≤k≤c。这个实验皿已经很久没有人用了,现在,小T被导师要求将其中一些单位立方体区域进 行消毒操作(每个区域可以被重复消毒)。而由于严格的实验要求,他被要求使用一种特定 的F试剂来进行消毒。 这种F试剂特别奇怪,每次对尺寸为xyz的长方体区域(它由xyz个单位立方体组 成)进行消毒时,只需要使用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)需要被消毒。输入保证满足abc≤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试剂

solution

抽丝剥茧
先思考在二维平面消毒时,怎么做??
显然,是行列二分图跑最大匹配的板

那么将这个搬到三维,可咱不会三位最大匹配啊!
在这里插入图片描述
a b c ≤ 500 abc\le500 abc500,所以至少有一个是 < 17 <17 <17
又可以考虑状压了
在这里插入图片描述
假设 a , b , c a,b,c a,b,c中最小的为 a a a
那么枚举状态,如果二进制对应位为 1 1 1,表示直接把这一层给削去
厚度为 1 1 1的消毒剂可以做到
剩下的没有削的层,拍扁成二维平面跑最大匹配

code

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 5005
vector < int > G[maxn];
int D, a, b, c, ans, tot;
int match[maxn];
int virus[4][maxn];
bool vis[maxn], flag[maxn];

bool find( int u ) {
	for( int i = 0;i < G[u].size();i ++ ) {
		int v = G[u][i];
		if( vis[v] ) continue;
		vis[v] = 1;
		if( ! match[v] || find( match[v] ) ) {
			match[v] = u;
			return 1;
		}
	}
	return 0;
}

void solve( int s ) {
	memset( match, 0, sizeof( match ) );
	for( int i = 1;i <= b;i ++ ) G[i].clear();
	int cnt = 0;
	for( int i = 0;i < a;i ++ )
		if( ( 1 << i ) & s ) flag[i + 1] = 0, cnt ++;
		else flag[i + 1] = 1;
	for( int i = 1;i <= tot;i ++ )
		if( flag[virus[1][i]] ) G[virus[2][i]].push_back( virus[3][i] );
	for( int i = 1;i <= b;i ++ ) {
		memset( vis, 0, sizeof( vis ) );
		if( find( i ) ) cnt ++;
	}
	ans = min( ans, cnt );
}

int main() {
	scanf( "%d", &D );
	while( D -- ) {
		tot = 0, ans = 0x7f7f7f7f;
		scanf( "%d %d %d", &a, &b, &c );
		for( int i = 1;i <= a;i ++ )
			for( int j = 1;j <= b;j ++ )
				for( int k = 1, x;k <= c;k ++ ) {
					scanf( "%d", &x );
					if( ! x ) continue;
					++ tot;
					virus[1][tot] = i;
					virus[2][tot] = j;
					virus[3][tot] = k;
				}
		int minn = min( a, min( b, c ) );
		if( minn == b ) swap( a, b ), swap( virus[1], virus[2] );
		else if( minn == c ) swap( a, c ), swap( virus[1], virus[3] );
		for( int i = 0;i < ( 1 << a );i ++ )
			solve( i );
		printf( "%d\n", ans );
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值