弱化一下,先考虑在二维上解决问题。
题目就转化为:有 \(n\) 个点 \((i, j)\) 需要被覆盖,而我们每次可以选一行或一列去覆盖,求覆盖所有点的最少选择次数。
如果我们对于每一个 \((i, j)\),我们把第 \(i\) 行和第 \(j\) 列连边,显然能构成一张二分图。
图中每一条边就是一个需求,而每选择一个点就能解决掉所有与之相连的需求,答案就是解决所有需求最少需要选择的点数。这就是二分图上的最小点覆盖问题。
答案即为最大匹配数。
现在加入三维。因为 \(a, b, c \leq 5 \times 10 ^ 3\),所以 \(a, b, c \leq 13\)。
那么我们可以考虑用最多 \(2^{13}\) 的时间去枚举其中一维的选择,即枚举这一维上我们选择哪几条基准线先直接覆盖。
那么剩下的就是之前的二维做法了。注意每次枚举的时候应该枚举最小的那一位,这样才能保证复杂度。
二分图最大匹配使用匈牙利算法,在接近完全图的图中性能相比于 Dinic 会较好。
#include <cstdio>
int Abs(int x) { return x < 0 ? -x : x; }
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; }
int read() {
int x = 0, k = 1;
char s = getchar();
while(s < '0' || s > '9') {
if(s == '-')
k = -1;
s = getchar();
}
while('0' <= s && s <= '9') {
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * k;
}
void write(int x) {
if(x < 0) {
x = -x;
putchar('-');
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void print(int x, char s) {
write(x);
putchar(s);
}
const int MAXN = 5e3 + 5;
const int MAXM = 2e5 + 5;
const int MAXL = 5e3 + 5;
const int INF = 2147483647;
struct edge {
int v, w, nxt;
edge() {}
edge(int V, int W, int Nxt) {
v = V, w = W, nxt = Nxt;
}
} e[MAXM << 1];
int head[MAXN], n, m, cnt;
void Add_Edge(int u, int v, int w) {
e[cnt] = edge(v, w, head[u]);
head[u] = cnt++;
}
bool Chose[MAXM << 1];
int Mat[MAXN], Tim[MAXN], tot;
void init(int N, int M) {
for(int i = 0; i <= cnt; i++)
Chose[i] = false;
n = N, m = M;
for(int i = 1; i <= n; i++)
head[i] = -1, Tim[i] = 0, Mat[i] = 0;
cnt = 0, tot = 0;
}
bool dfs(int u) {
if (Tim[u] == tot)
return false;
Tim[u] = tot;
for (int i = head[u], v; ~i; i = e[i].nxt) {
if(Chose[e[i].w])
continue;
v = e[i].v;
if (!Mat[v] || dfs(Mat[v])) {
Mat[v] = u;
return true;
}
}
return false;
}
int calc() {
int ans = 0;
for (int i = 1; i <= m; i++)
Mat[i] = 0;
for (int i = 1; i <= n; i++)
Tim[i] = 0;
for (int i = n; i >= 1; i--) {
tot++;
ans += dfs(i);
}
return ans;
}
bool vis[MAXN];
int q[MAXN], pos[5], len = 0, ans = INF, tot2 = 0, S, T;
void dfs2(int p) {
if(p > pos[1]) {
ans = Min(ans, calc() + tot2);
return ;
}
Chose[p] = true;
tot2++;
dfs2(p + 1);
Chose[p] = false;
tot2--;
dfs2(p + 1);
}
int main() {
int t = read();
while(t--) {
for(int i = 1; i <= 3; i++)
pos[i] = read();
if(pos[1] > pos[2])
pos[1] ^= pos[2] ^= pos[1] ^= pos[2];
if(pos[2] > pos[3])
pos[2] ^= pos[3] ^= pos[2] ^= pos[3];
if(pos[1] > pos[2])
pos[1] ^= pos[2] ^= pos[1] ^= pos[2];
init(pos[2], pos[3]);
len = 0, ans = INF, tot = 0;
for(int i = 1, j, k, p; i <= pos[1]; i++)
for(j = 1; j <= pos[2]; j++)
for(k = 1; k <= pos[3]; k++) {
p = (i - 1) * pos[2] * pos[3] + (j - 1) * pos[3] + k;
q[p] = read();
if(q[p])
Add_Edge(j, k, i);
}
dfs2(1);
print(ans, '\n');
}
return 0;
}