題目鏈接:291. 蒙德里安的夢想
求把NM的棋盤分割成若干個12的的長方形,有多少種方案。
例如當N=2,M=4時,共有5種方案。當N=2,M=3時,共有3種方案。
如下圖所示:(圖在鏈接裏.)
輸入格式
輸入包含多組測試用例。
每組測試用例佔一行,包含兩個整數N和M。
當輸入用例N=0,M=0時,表示輸入終止,且該用例無需處理。
輸出格式
每個測試用例輸出一個結果,每個結果佔一行。
數據範圍
1≤N,M≤11
輸入樣例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
輸出樣例:
1
0
1
2
3
5
144
51205
程序說明:
核心思想是先放橫着的長方形,放好以後再插入豎着的長方形。那麼總方案數就等於只放橫着的小方塊的合法方案數。
f[i][j]表示已經將 i - 1 列擺好,且從第 i - 1 列伸出到第 i 列的所有方案的狀態是 j 。其中 j 是二進制數,j 的每一位對應網格中每一列,如果前一列伸出到第 i 列,則 j 爲1,反之爲0。
狀態的轉移需要看前一列的情況,即f[i - 1][k],其中k表示第 i - 2列伸出到第 i - 1 列的情況。只有當 j 和 k 不同時爲1,即放橫着的長方形不衝突時才能進行下去。
判斷當前方案是否合法需要看每一列的空白部分是否爲偶數,如果是偶數的話,才能放入豎着的長方形。
放完m - 1列就已經全部放完了,最後的答案是f[m][0](即前m - 1列已經放完,且沒有長方形伸出到外面去)。
代碼如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 12, M = 1 << N;
long long f[N][M];
int n, m, st[M]; //st[]儲存是否能放入豎着的長方形
int main() {
while(cin>>n>>m && n || m) {
//預處理出每種情況下是否能放入豎着的長方形
for(int i = 0; i < 1 << n; i++) {
st[i] = 1;
int cnt = 0;
for(int j = 0; j < n; j++) {
if(i >> j & 1) { //如果這一位是1,即已經被佔了
if(cnt & 1) { //空白部分爲奇數
st[i] = 0;
cnt = 0;
}
}
else
cnt++;
}
if(cnt & 1)
st[i] = 0;
}
memset(f, 0, sizeof f);
f[0][0] = 1; //空白的方案數是1
for(int i = 1; i <= m; i++)
for(int j = 0; j < 1 << n; j++)
for(int k = 0; k < 1 << n; k++) //k枚舉前一列的情況
if((j & k) == 0 && st[j | k])
f[i][j] += f[i - 1][k];
cout<<f[m][0]<<endl;
}
return 0;
}