1. 程式人生 > >Mondriaan's Dream【狀壓DP】

Mondriaan's Dream【狀壓DP】

測評傳送門

題意:

給定一個 n*m 的矩形,用1*2的方塊填充的所有方案數

 

 

Sample Input

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

Sample Output

1
0
1
2
3
5
144
51205

思路:

像這樣看上去就不會做的題目資料範圍一般都不大(因為要用狀壓呀,大了能存的下嗎?) 

而且顯然答案會很大,所以記得開 long long 

正題----

我們可以一行一行地看,對於上一行地情況,可以是橫著的也可以是豎著的,橫著的對我們當前這一行的狀態無影響,關鍵是豎著的方塊我們在這一行必須給它接上。

我們用長度為 m 的二進位制數來表示狀態

  狀態為豎著擺時的上半截,狀態為 1 ,否則為 0

我們考慮當前這一行,如果上一行是1,我必須接上下半截,狀態為 0 ;如果是 0 ,那我這一行就可以隨便擺 

設當前狀態為 j,上一行狀態為 k

所以必定滿足:

  1. j&k==0
  2. j | k 狀態中連續的 0 必須有偶數個

第二條也很顯然,我們活下來的是什麼狀態呢?1表示豎著,0,表示橫著且連續,如果 0 是奇數個,就不符合要求

所以我們就先需要預處理出合法的情況

code

#include<stdio.h> 
#include<algorithm> 
using
namespace std; int n,m; long long f[12][1<<11]; bool ins[1<<11]; int main() { while(scanf("%d%d",&n,&m) && n && m) { int mix=(1<<m); for(int i=0;i<mix;++i) { bool cnt=0,flag=0; for(int j=0;j<m;++j) {
if(i>>j & 1) flag|=cnt,cnt=0; else cnt^=1; } ins[i]=flag|cnt?0:1; } f[0][0]=1; for(int i=1;i<=n;++i) { for(int j=0;j<mix;++j) { f[i][j]=0; for(int k=0;k<mix;++k) { if((j&k)==0 && ins[j|k]) f[i][j]+=f[i-1][k]; } } } printf("%lld\n",f[n][0]); } return 0; }