[Hnoi2013]消毒

来源:互联网 发布:东京奥运会 知乎 编辑:程序博客网 时间:2024/04/28 19:32

[Hnoi2013]消毒

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


题解转的!!

首先由均值不等式可以得到,min{a, b, c} <= 17,不妨假设最短的那条边为a。接下来考虑消除,答案肯定不会超过a,因为可以做若干次1*b*c的消除。

一开始看到这个结论我还是非常傻逼的以为,只要把需要消毒的每一片1*b*c都消毒即可,还傻傻问副队哪来的二分图……经过副队的教导才意识到这样不一定最优,比较科学的做法是:2^a枚举每层是否消掉,然后用a*1*ca*b*1的片覆盖,也就是转成二维的最小棋盘覆盖。

所谓最小棋盘覆盖就是说,每次可以覆盖掉棋盘的一整行或一整列,问把所有标记点覆盖的最小次数。嗯,这个也不会做……Google了一下,看到搜索结果里面最小点覆盖行列拆开这两个关键词才突然意识到,这不就是个水水的二分图匹配嘛。

考虑把所有行放在左边,所有列放在右边,然后如果(i, j)需要被覆盖,那么连边(Xi, Yj)。之后就是一个最小点覆盖的问题了(选择最少的点,使得每条边都至少有一个端点被选择),因为最小点覆盖=最大匹配,所以轻松做掉了。(这个时候突然很庆幸前不久刚学匈牙利,明显这种时候用匈牙利跑匹配比网络流省事多了=v=)

常数

虽然写程序的时候已经在清数组的时候注意了,但是手测第一个点还是TLE得非常严重(吐槽一下,所有数据都是10K……)。后来看到副队博客上还有一句话,发现有一个非常卡BUG的优化方法:因为1的数量非常少,所以可以把1用链表串起来。嗯,加了优化后就可以过了。不知道不卡BUG是如何做的= =?


#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<algorithm>#include<iostream>using namespace std;const int maxn = 5000;const int maxm = 30;int a, b, c;class Dread{    private:        bool isdigit(char ch) { return ch >= '0' && ch <= '9'; }        bool isalpha(char ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); }        void Getchar(int &tmp){            char ch; tmp = 0; bool b = true;            while (ch = getchar()){if (ch == '-') b = false;if (isdigit(ch)) break;            }for (; isdigit(ch); ch = getchar()) tmp = tmp * 10 + ch - '0';        if (!b) tmp = -tmp;}        void Getchar(char &tmp){            while (tmp = getchar()) if (isalpha(tmp)) break;        }    public:        int Int(){ int x; Getchar(x); return x; }        char Ch(){ char x; Getchar(x); return x; }}Read;struct Tpoint{int x, y, z;Tpoint() {}Tpoint(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {if (a <= b && a <= c) swap(x, x);else if (b <= a && b <= c) swap(x, y);else if (c <= a && c <= b) swap(x, z);}}point[maxn];int pre[maxn], now[maxn], son[maxn], v[maxn];int sum, tot;void cc(int a, int b, int c){pre[++ tot] = now[a];now[a] = tot;son[tot] = b;v[tot] = c;}void init(){a = Read.Int(), b = Read.Int(), c = Read.Int();sum = 0;for (int i = 1; i <= a; i ++)for (int j = 1; j <= b; j ++)for (int k = 1; k <= c; k ++){int x = Read.Int();if (x) point[++ sum] = Tpoint(i, j, k);}if (a <= b && a <= c) swap(a, a);else if (b <= a && b <= c) swap(a, b);else if (c <= a && c <= b) swap(a, c);tot = 0; memset(now, 0, sizeof(now));for (int i = 1; i <= sum; i ++)cc(point[i].y, point[i].z, point[i].x);}bool used[maxm], vis[maxm];int l[maxm];bool find(int x){for (int p = now[x]; p; p = pre[p]){if (!used[v[p]] && !vis[son[p]]){vis[son[p]] = true;if (!l[son[p]] || find(l[son[p]])){l[son[p]] = x;return true;}}}return false;}int ans = 0;void dfs(int x, int num){if (num >= ans) return;if (x > a){int s = 0;memset(l, 0, sizeof(l));for (int i = 1; i <= b; i ++){memset(vis, 0, sizeof(vis));s += find(i);if (s + num >= ans) return;}ans = num + s;}dfs(x + 1, num);used[x] = 1; dfs(x + 1, num + 1);used[x] = 0;}void work(){ans = a;dfs(1, 0);cout <<ans <<endl;}int main(){int t = Read.Int();while (t --){init(); work();}return 0;}

0 0
原创粉丝点击