洛谷 P3231 [HNOI2013]消毒 题解
一、题目:
二、思路:
这道题作为一道省选题出的非常优秀。反正我是不会做。
首先本题是对二维模型的扩展。
想一下二维的情况该怎么做。我们把行号、列号看做两排点,如果一个点\((x,y)\)需要消毒,就连从\(x\)向\(y\)连一条边。
那么这张图显然是一张二分图。
再考虑一下题目的性质。我们会发现单行、单列的消毒一定不会比用几个矩形消毒更劣。
所以二维的情况就转化成用最少的行、列将需要被消毒的点覆盖。这是一个二分图的最小点覆盖模型。直接在我们刚才建好的图上跑二分图匹配即可。
再来考虑三维。突然感觉难度上升了一个台阶,直接做并不好做。
再来挖掘题目性质。注意到\(abc\leq 5000\),设\(z = \min\{a,b,c\}\),则\(z\leq\sqrt[3]{5000}<18\)。
所以我们把最小的那维拿出来当做长方体的高。爆搜出哪些层不选,哪些层选。
对于每个不选的层,我们直接用一个高为1的长方体覆盖即可。
对于选择的层,我们把这些层拍扁到一层,用二维的方法去做。对应到原来的三维情况就是,我们用一些长为1或宽为1的扁长方体把点覆盖掉。这样的话是最优的。
三、细节
本题比较玄学。首先需要注意数组的大小。
我因为数组开过大,导致程序不能运行。我还以为是我家电脑坏了,就重装了一下电脑。结果换了台电脑,发现程序还是不能运行,这才发现是我程序的问题!
然后注意数组尽量不要memset
,常数会很大。
最后就是Dinic好像还没有匈牙利快。我搞不懂是为什么。
四、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <ctime>
using namespace std;
#define LL long long
#define FILEIN(s) freopen(s".in", "r", stdin)
#define FILEOUT(s) freopen(s".out", "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
#define x first
#define y second
inline LL read(void) {
LL x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int maxn = 5e3 + 5, inf = 1e8;
int X, Y, Z, head[maxn << 1], tot;
int map[maxn][72][18];
int tmp[maxn][72], match[maxn << 1];
bool vis[maxn << 1];
struct Edge {
int y, next;
Edge() {}
Edge(int _y, int _next) : y(_y), next(_next) {}
}e[maxn];
inline void add(int x, int y) {
e[++ tot] = Edge(y, head[x]);
head[x] = tot;
}
inline void init(void) {
pair<int, int> a[4];
int p[4];
for (int i = 1; i <= 3; ++ i) a[i].x = read(), a[i].y = i;
if (a[1].x < a[2].x) swap(a[1], a[2]);
if (a[1].x < a[3].x) swap(a[1], a[3]);
if (a[2].x < a[3].x) swap(a[2], a[3]);
for (int i = 1; i <= 3; ++ i) p[a[i].y] = i;
X = a[1].x; Y = a[2].x; Z = a[3].x;
for (int i = 1; i <= a[p[1]].x; ++ i)
for (int j = 1; j <= a[p[2]].x; ++ j)
for (int k = 1; k <= a[p[3]].x; ++ k) {
if (p[1] == 1) {
if (p[2] == 2) map[i][j][k] = read();
else map[i][k][j] = read();
} else if (p[1] == 2) {
if (p[2] == 1) map[j][i][k] = read();
else map[k][i][j] = read();
} else {
if (p[2] == 1) map[j][k][i] = read();
else map[k][j][i] = read();
}
}
}
bool dfs(int x) {
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (!vis[y]) {
vis[y] = true;
if (!match[y] || dfs(match[y])) {
match[y] = x; return true;
}
}
}
return false;
}
int main() {
int Q = read();
while (Q --) {
init();
int ans = 0x3f3f3f3f;
for (int s = 0; s < (1 << Z); ++ s) {
int sum = 0;
for (int i = 1; i <= X; ++ i) head[i] = 0;
for (int i = 1; i <= Y; ++ i) match[i + X] = 0;
tot = 0;
for (int i = 1; i <= X; ++ i)
for (int j = 1; j <= Y; ++ j)
tmp[i][j] = 0;
for (int k = 1; k <= Z; ++ k) {
if (!((s >> (k - 1)) & 1)) ++ sum;
else {
for (int i = 1; i <= X; ++ i)
for (int j = 1; j <= Y; ++ j)
tmp[i][j] |= map[i][j][k];
}
}
if (sum > ans) continue;
for (int i = 1; i <= X; ++ i)
for (int j = 1; j <= Y; ++ j)
if (tmp[i][j]) add(i, j + X);
for (int i = 1; i <= X; ++ i) {
for (int j = 1; j <= Y; ++ j)
vis[j + X] = false;
if (dfs(i)) ++ sum;
}
ans = min(ans, sum);
}
printf("%d\n", ans);
}
return 0;
}