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;
}

浙公网安备 33010602011771号