「HNOI2013」消毒

弱化一下,先考虑在二维上解决问题。

题目就转化为:有 \(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;
}
上一篇:容器


下一篇:《PyQt5高级编程实战》自定义信号详解