1. 棋盤問題
1.1 蒙德里安的夢想
將一種狀態用二進位制來表示。
對於此題,由於小方塊是1*2
,如果先擺放橫著的,
故在擺放第i
列時,只需要考慮第i-1
列有沒有衝突的。
然後在插空擺放豎著的,如果橫向擺放已經確定,
那麼豎向的也是確定的,即:總方案數 = 橫向擺放的方案數。
要判斷擺放是否合法,即不能有空餘的小方塊。
那麼:在擺放第i
列豎向的時候,需要判斷,第i
列中的數個連續的的空白位置,
是否是偶數個( 偶數個才能正好將豎向的方塊擺滿)。
對於一列來說,共有2
n2^n
2n個狀態的可能。
對於i
列是否與i-1
列衝突,j&k == 0
(k
為第i-1
列的狀態(確定合法),j
為第i
列的狀態(不確定是否合法))
對於判斷某乙個狀態中的空白位置是否是偶數個,預處理即可——遍歷2
n2^n
2n個狀態,將每種狀態是否可行,放到dp中(只是乙個標記陣列),j|k
是第i
列 和第i-1
列合起來之後的狀態,看這個狀態的空白位置是否合法。
#include #include #include using namespace std;
const int n = 12, m = 1 << n; // 一位代表當前行的狀態 所以要1 << n 個狀態
int n, m;
long long f[n][m];
bool dp[m]; // 標記此狀態下空餘的格仔數是否是偶數個 偶數個就可以豎著放
int main()
cnt = 0;
}else
cnt++;
// 最後乙個
if (cnt % 2 == 1)
dp[i] = false;
}memset(f, 0, sizeof f);
f[0][0] = 1;
// m列
for (int i = 1; i <= m; i++)
// 列舉i列每一種狀態
for (int j = 0; j < 1 << n; j++)
// 列舉i-1列每一種狀態
for (int k = 0; k < 1 << n; k++)
if ((j & k) == 0 && dp[j | k])
f[i][j] += f[i - 1][k]; //那麼這種狀態下它的方案數等於之前每種k狀態數目的和
cout << f[m][0] << endl;
}return 0;
}
狀態壓縮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...
15 蒙德里安的夢想 狀態壓縮DP
位運算 二進位制表示狀態 狀態壓縮dp 先把橫著的小方塊放好,然後剩下位置用豎著的小方塊填充 然後就轉化為求橫著擺放小方塊的方案數 按列來求 狀態表示 dp i j 表示所有擺到了第i列,然後上一列伸出來的小方塊的狀態是j的情況下,總的方案數 狀態轉移 列舉一下i 1列的狀態 比如說當前狀態是j 0...
蒙德里安的夢想
求把n m的棋盤分割成若干個1 2的的長方形,有多少種方案。例如當n 2,m 4時,共有5種方案。當n 2,m 3時,共有3種方案。如下圖所示 輸入格式 輸入包含多組測試用例。每組測試用例佔一行,包含兩個整數n和m。當輸入用例n 0,m 0時,表示輸入終止,且該用例無需處理。輸出格式 每個測試用例輸...