[LeetCode 双周赛25] 4. 每个人戴不同帽子的方案数(状态压缩、动态规划、巧妙解法)

文章目录

    • 1. 题目来源
    • 2. 题目说明
    • 3. 题目解析
      • 方法一:状压dp+巧妙解法

1. 题目来源

链接:1434. 每个人戴不同帽子的方案数

2. 题目说明

[LeetCode 双周赛25] 4. 每个人戴不同帽子的方案数(状态压缩、动态规划、巧妙解法)_第1张图片
[LeetCode 双周赛25] 4. 每个人戴不同帽子的方案数(状态压缩、动态规划、巧妙解法)_第2张图片

3. 题目解析

方法一:状压dp+巧妙解法

又是一道状压 dp 问题。一开始直观的思路是直接对帽子进行性状态压缩,让人去找帽子。但是这个帽子的数量太多了,不利用直接进行状态压缩,但是人的数量很少,可以对人进行状态压缩,让帽子去找人。思路如下:

  • dp[i][bits] 前 i 顶帽子确定了归属,人带帽子的状态 bits 的方案数
  • dp[i][bits]->dp[i+1][new_bits] 转态转移两种情况:
    • 我们将 i+1 顶帽子,给某个人 jnew_bits = bits | (1<,前提是 j 喜欢帽子 i + 1(bits>>j) & 1 = 0
    • i+1 顶帽子不给人带,bits=new_bits

其它相关注释也写在代码里了,便于查看。我感觉状压 dp 就选择数据小的那一维进行状压就可了。但目前却是还是理解不到位的。

参见代码如下:

// 执行用时 :508 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :7.9 MB, 在所有 C++ 提交中击败了100.00%的用户

const int MOD = 1e9 + 7;
int dp[45][1<<10];

class Solution {
public:
    int numberWays(vector<vector<int>>& hats) {
        int n = hats.size(); 
        int lim = 1 << n;
        for (int i = 0; i <= 40; ++i) for (int j = 0; j < lim; ++j) dp[i][j] = 0;
        dp[0][0] = 1;

        for (int h = 1; h <= 40; ++h) {             // 分配帽子
            for (int s = 0; s < lim; ++s) {         // 当前人带帽子的状态
                if (dp[h - 1][s]  == 0) continue;   // 在模下不会对结果产生影响,直接continue
                for (int i = 0; i < n; ++i) {       // 否则尝试让i人带这个帽子
                    bool flag = false;
                    for (auto e : hats[i]) if (e == h) flag = true;     // 查看是否为i人所喜欢的帽子
                    if (flag == false) continue;    // 不喜欢查看下一个人
                    if ((s >> i) & 1) continue;     // i这个人不能带过帽子
                    int news = s | (1 << i);        // i带这个帽子,并更新它的状态
                    dp[h][news] = (dp[h][news] + dp[h - 1][s]) % MOD;   // 第h顶帽子有人带了状态转移
                }
            }
            for (int s = 0; s < lim; ++s) {         // 若第h顶帽子没人带状态转移
                dp[h][s] = (dp[h][s] + dp[h - 1][s]) % MOD;
            }
        }
        return dp[40][lim - 1];
    }
};

你可能感兴趣的:(LeetCode周赛)