45 Bugs Intergrated,Inc

Bugs Intergrated,Inc.

题面

给定一个 \(n \times m\) 的网格图,图中有一些坏点

要从图中切割出尽可能多的 \(2 \times 3\)\(3 \times 2\) 的矩形,矩形中不能包含坏点,求最多能切割出几块矩形?

\(1 \le n \le 150,\ 1 \le m \le 10\)

题解

这道题用普通的状压dp好像不太好做

但是可以用一种比较trick的状压dp解法去做,这种方法类似炮兵阵地一题的解法2,我们且称它为“搜索状压”

这种解法的核心是为每个位置赋予一个属性,得以区分不同位置的差异

对于当前行的一个位置来说,我们需要考虑前面的行对当前行的影响

我们定义几个位置,因为是 \(2 \times 3\) 或者是 \(3 \times 2\) 的矩形,所以我们最多要知道三个位置的状态,我们用三进制来表示

  • 0当前行以及上一行的此位置没有被占用
  • 1当前行的这个位置没有被占用,上一行的这个位置被占用
  • 2当前行的这个位置被占用/坏点

那么 \(3 \times 2\) 的矩形能够放置,当且仅当 \(i,i + 1\) 的当前行以及上一行都是 0 ,\(2 \times 3\) 的矩形能够放置,当且仅当 \(i,i + 1,i+2\) 的当前行都是 0

对于每一行,我们枚举出上一行的某个合法状态,然后递推出当前行的基本状态,也就是 \(2 \to 1,1 \to 0, 0 \to 0\)

然后再在这个基本状态的基础上尝试填格子,从而进行转移

试填格子的过程根据我们刚才的推论,枚举每个位置试填即可

这个过程可以用 \(dfs\) 来做,所以叫“搜索状压”

总时间复杂度上界为 \(O(nm3^{2m})\) 但是由于 \(dfs\) 函数中的剪枝,实际跑起来很快,严格的证明我不会啊

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 155, M = 15;

int n, m, k;
int cur[M], pre[M], bad[N][M], f[2][60000];
int pw[M];

int thr_ten (int *a) {
    int res = 0;
    for (int i = 0; i < m; i ++) {
        res += a[i] * pw[i];
    }
    return res;
}

void ten_thr (int x, int *a) {
    for (int i = 0; i < m; i ++) {
        a[i] = x % 3;
        x /= 3;
    }
}

void dfs (int i, int j, int cnt, int state) {
    f[i][state] = max (f[i][state], cnt);
    if (j >= m) return;

    //放 2 * 3
    if (j + 1 < m && !cur[j] && !cur[j + 1] && !pre[j] && !pre[j + 1]) {
        cur[j] = cur[j + 1] = 2;
        dfs (i, j + 2, cnt + 1, thr_ten (cur));
        cur[j] = cur[j + 1] = 0;
    }

    //放 3 * 2
    if (j + 2 < m && !cur[j] && !cur[j + 1] && !cur[j + 2]) {
        cur[j] = cur[j + 1] = cur[j + 2] = 2;
        dfs (i, j + 3, cnt + 1, thr_ten (cur));
        cur[j] = cur[j + 1] = cur[j + 2] = 0;        
    }
    
    //不放
    dfs (i, j + 1, cnt, state);
}


void solve () {
    memset (bad, 0, sizeof bad);
    cin >> n >> m >> k;

    //为了方便,我们定义 1 ~ n 行,0 ~ m-1 列
    for (int i = 1; i <= k; i ++) {
        int x, y;
        cin >> x >> y;
        bad[x][y - 1] = 1;
    }

    memset (f, -1, sizeof f);
    for (int i = 0; i < m; i ++) {
        pre[i] = bad[1][i] ? 2 : 1;
    }
    int now = 1, state = thr_ten (pre);
    f[now][state] = 0;

    for (int i = 2; i <= n; i ++) {
        now ^= 1;
        memset (f[now], -1, sizeof f[now]);
        for (int j = 0; j < pw[m]; j ++) {
            if (f[now ^ 1][j] == -1) continue;
            ten_thr (j, pre);
            for (int k = 0; k < m; k ++) {
                if (bad[i][k]) cur[k] = 2;
                else cur[k] = pre[k] ? pre[k] - 1 : 0;
            }
            state = thr_ten (cur);
            //相当于是在 f[i - 1][j] 的基础上进行转移
            dfs (now, 0, f[now ^ 1][j], state);
        }
    }
    int ans = 0;
    for (int i = 0; i < pw[m]; i ++) {
        ans = max (ans, f[now][i]);
    }
    cout << ans << endl;
}

int main () {

    pw[0] = 1;
    for (int i = 1; i <= 10; i ++) {
        pw[i] = pw[i - 1] * 3;
    }
    int T;
    cin >> T;
    while (T --) {
        solve ();
    }

    return 0;
}
posted @ 2025-10-09 21:21  michaele  阅读(10)  评论(0)    收藏  举报