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