位運算 + 二進位制表示狀態 = 狀態壓縮dp
先把橫著的小方塊放好,然後剩下位置用豎著的小方塊填充
然後就轉化為求橫著擺放小方塊的方案數
按列來求
狀態表示:
dp[i][j]表示所有擺到了第i列,然後上一列伸出來的小方塊的狀態是j的情況下,總的方案數
狀態轉移:
列舉一下i - 1列的狀態
比如說當前狀態是j = 00001
然後上乙個狀態i - 1是k = 10010
從i - 1列伸到第i列的:j = 00001
從i - 2列伸到第i - 1列的:k = 10010
位運算的預備知識
與運算 &
或運算 |
條件一:不能有衝突。
也就是需要判斷(j & k)== 0,若等於0,表示沒有衝突
條件二:第i - 1列空餘格仔一定是連續的,且是偶數,因為要豎著放小方塊
j | k裡面,所有0的位置,就是第i - 1列所有空白的格仔
就是j | k裡面不能存在連續奇數個0
只要滿足這兩個條件,就可以轉移過來
把所有滿足條件的k加起來,就是狀態轉移方程
狀態數量:11 * 2 ^ 11
轉移數量:2 ^ 11
時間複雜度:11 * 2 ^ 11 *2 ^ 11 = 4 * 10 ^ 7
2023年7月31日複習更新:關於dp[0][0]為什麼初始化為1
在下面**31行,求dp陣列的過程時,是從第1列開始列舉的,但是之後需要用到dp[i - 1]
從dp陣列的定義入手考慮,dp[i][j]表示所有擺到了第i列,然後上一列伸出來的小方塊的狀態是j的情況下,總的方案數
i的含義是這是第i列
j的含義是從第i - 1列伸出來的小方塊的狀態是j
當i = 0時,i - 1 = -1,第-1列不會有小方塊,所以第i列的狀態一定是0
dp[0][0] = 1表示不放也是一種方案,這是dp問題統計方案數的重點
遇到1以後,連續奇數個0已經結束了,新開始一段
20 } else23}
24if (cnt & 1) 27}
28//
然後是dp的過程
29 memset(dp, 0, sizeof dp); //
把dp陣列置為0
30 dp[0][0] = 1; //
邊界情況
31for (int i = 1; i <= m; i++) 37}
38}39}
40 cout << dp[m][0] << endl; //
答案41}42
return0;
43 }
狀態壓縮dp 蒙德里安的夢想
1 n,m 11 輸入樣例 1 21 3 1 42 2 2 32 4 2 11 4 11 0 0輸出樣例 include using namespace std const int n 12,m 1 n int st m long long f n m intmain else cnt if cnt...
狀態壓縮DP例題 蒙德里安的夢想
1.棋盤問題 1.1 蒙德里安的夢想 將一種狀態用二進位制來表示。對於此題,由於小方塊是1 2,如果先擺放橫著的,故在擺放第i列時,只需要考慮第i 1列有沒有衝突的。然後在插空擺放豎著的,如果橫向擺放已經確定,那麼豎向的也是確定的,即 總方案數 橫向擺放的方案數。要判斷擺放是否合法,即不能有空餘的小...
蒙德里安的夢想
求把n m的棋盤分割成若干個1 2的的長方形,有多少種方案。例如當n 2,m 4時,共有5種方案。當n 2,m 3時,共有3種方案。如下圖所示 輸入格式 輸入包含多組測試用例。每組測試用例佔一行,包含兩個整數n和m。當輸入用例n 0,m 0時,表示輸入終止,且該用例無需處理。輸出格式 每個測試用例輸...