1. 题目来源
2. 题目说明
3. 题目解析
方法一:状压dp+巧妙解法
又是一道状压 dp
问题。一开始直观的思路是直接对帽子进行性状态压缩,让人去找帽子。但是这个帽子的数量太多了,不利用直接进行状态压缩,但是人的数量很少,可以对人进行状态压缩,让帽子去找人。思路如下:
- dp[i][bits] 前
i
顶帽子确定了归属,人带帽子的状态bits
的方案数 - dp[i][bits]->dp[i+1][new_bits] 转态转移两种情况:
- 我们将
i+1
顶帽子,给某个人j
,new_bits = bits | (1<<j)
,前提是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];
}
};