[每日一题] LCP 04. 覆盖

328 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

LCP 04. 覆盖

你有一块棋盘,棋盘上有一些格子已经坏掉了。你还有无穷块大小为12的多米诺骨牌,你想把这些骨牌不重叠地覆盖在完好的格子上,请找出你最多能在棋盘上放多少块骨牌?这些骨牌可以横着或者竖着放。你有一块棋盘,棋盘上有一些格子已经坏掉了。\\ 你还有无穷块大小为1 * 2的多米诺骨牌,\\ 你想把这些骨牌不重叠地覆盖在完好的格子上,\\ 请找出你最多能在棋盘上放多少块骨牌?\\ 这些骨牌可以横着或者竖着放。

 

输入:n,m代表棋盘的大小;broken是一个b×2的二维数组,其中每个元素代表棋盘上每一个坏掉的格子的位置。n, m代表棋盘的大小;broken是一个 b \times 2的二维数组,\\ 其中每个元素代表棋盘上每一个坏掉的格子的位置。

输出:一个整数,代表最多能在棋盘上放的骨牌数。一个整数,代表最多能在棋盘上放的骨牌数。

 

示例 1:
输入:n = 2, m = 3, broken = [[1, 0], [1, 1]]
输出:2
解释:我们最多可以放两块骨牌:[[0, 0], [0, 1]]以及[[0, 2], [1, 2]]。(见下图)

image.png

 

示例 2:
输入:n = 3, m = 3, broken = []
输出:4
解释:下图是其中一种可行的摆放方式

image.png

 

数据范围:
  • 1 <= n <= 8
  • 1 <= m <= 8
  • 0 <= b <= n * m
思路

状压dp

这个题和蒙德里安的梦想有点像
只不过,不同的地方是这个题多了个禁用方块且是计算放置最大的数量

考虑一个这样子的关系,对于所有的状态 n行一共有 2n2^n个状态
对于每一行来说,可以枚举推出下一行的所有状态 2n×2n×m2^n \times 2^n \times m的复杂度
首先,不考虑不可用的格子。我们应该如何转移?
我们令 二进制中的0代表这一个格没有突出,1代表这一格突出了
比如 001000,此时只有001000这一个位置可以横着放一个,突出出去了
在放置时,要保证 前面那一行对应位置是有空位的,也就是 a & b 是 等于0的
然后方完横的以后,接下来就看一下当前行还可以放置几个竖立的
在统计没有突出的格子的时候,要考虑前一行突出来的 也就是 a | b这样
比如 01010101 这样的就一个也放不了 而 001001001 则可以放置3个多米诺骨牌
所以状态转移就出来了,即:
dp[i][now]=max(dp[i][now],dp[i1][pre]+cnt0+count(b))dp[i][now] = max(dp[i][now], dp[i-1][pre] + cnt_0 + count(b))
然后我们把broken这个限制再加上就可以了

代码
class Solution {
public:
    int domino(int n, int m, vector<vector<int>>& broken) {
        vector<int> b(m + 1);
        vector<vector<int>> state(m + 1);
        for (auto v : broken) b[1+v[1]] |= 1 << v[0];
        for (int i = 1; i <= m; i ++)
        for (int j = 0; j < 1 << n; j ++) {
            if (j & b[i] || (i < m && (j & b[i+1]))) continue;
            state[i].push_back(j);
        }
        auto count = [](int x) {
            int res = 0;
            while (x) res += x & 1, x >>= 1;
            return res;
        };
        state[0].push_back(0);
        vector<vector<int>> dp(m + 1, vector<int>(1 << n));
        for (int i = 1; i <= m; i ++)
        for (auto u : state[i])
        for (auto v : state[i-1]) {
            if (u & v) continue;
            int tag = u | v | b[i];
            int cnt = 0, cur = 0;
            for (int i = 0; i < n; i ++) {
                if (tag >> i & 1) cnt += cur / 2, cur = 0;
                else cur ++;
            }
            cnt += cur / 2;
            dp[i][u] = max(dp[i][u], dp[i-1][v] + cnt + count(v));
        }
        return dp[m][0];
    }
};