題意:給你一個1*2的矩陣進行填充,結果求填滿的種類數。
分析:下面是我引用過來的說明,很不錯。
最上面的爲第0行,最下面爲第n-1行
從上到下按行DP
其中一行的狀態我們用一個二進制表示,0表示沒有被覆蓋,1表示被覆蓋了
最後得到一個01串,這個串變回十進制就是一個狀態
定義狀態dp[i][s],表示前i-1行已經放滿,第i行的狀態爲s的方案數
狀態轉移方程爲 dp[i][s]=sum{ dp[i-1][ss] } ,其中狀態s與狀態ss兼容
這個狀態轉移方程的內涵在於理解s和ss何爲兼容
首先我們約定一個放置方法,就是豎着放的時候,我們暫且將其稱爲“上凸型擺放”
因爲豎放必然佔據第i-1行和第i行,我們約定這個方塊是屬於第i行的,也就是說它凸上去了
那麼要在第i行的第j列豎放一個方塊的話,第i-1行第j列必須沒有方塊
也就是說,第i行的放置是受到第i-1行的限制的,反過來說在第i行豎放了方塊,也會影響第i-1行的狀態
所以這樣就可以講解一下狀態轉移方程了,前i-2行已經放滿了,第i-1行的狀態爲ss(dp[i-1][ss])
此時在第i行開始放一些方塊,放的方法不定,可能橫放可能豎放,但是按這個方案放完後
第i-1行剛好被填滿,且第i行的狀態變爲了s,所以不難想到第i-1行的狀態ss到第i行的狀態s這個轉移是唯一的
所以有 dp[i][s]=sum{ dp[i-1][ss] }
最後我們詳細討論一下s和ss在什麼情況下是兼容的
1.第i行的第j列爲1,第i-1行的第j列爲1,這樣的話,說明第i行的第j列一定不是豎放而是橫放否則會與第i-1行的第j列衝突
所以馬上緊接着判斷第i行第j+1列,如果是1,那麼滿足橫放的規則,同時也要第i-1行第j+1列也要爲1,否則的話這個格子沒辦法填充,
成立後向左移動兩格
不滿足上述條件的,就是兩個不兼容或者不合法的狀態
2.第i行第j列爲1,第i-1行第j列爲0,那麼說明第i行第j列應該豎放並填充第i-1行第j列,成立後向左移動一格
3.第i行第j列爲0,說明不放方塊,那麼第i-1行第j列必須爲1,否則沒法填充這個格子。若第i-1行第j列也爲0,不兼容不合法
(至於第i行第j列這個格子空着幹什麼,其實就是留出來給第i+1行豎放的時候插進來的)
那麼目標狀態是什麼,就是dp[n][maxs],maxs表示全部是1的串,即第n-1行以上全部覆蓋滿,第n行的狀態爲maxs,即沒有空着的格子,也全部覆蓋滿了
即整個矩形全部被覆蓋滿了的狀態
最後是第1行的初始化問題,因爲約定了“上凸型擺放”,所以第1行是不能豎放方格的,只能橫放方格,
每橫放一個必定佔據兩個格子,所以在判斷一個狀態(那個01串)的時候,連着的1的個數必定爲偶數,如果出現了單獨的1,說明不合法
下面附代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1<<12;
ll dp[13][maxn];
int n,m;
bool judge(int val){
int flag=0;
while(val){
if(val%2){
flag=(flag+1)%2;
}
else{
if(flag==1){
return false;
}
}
val>>=1;
}
if(flag) return false;
return true;
}
bool ok(int tmp_1,int tmp_2){
for(int i=0;i<m;){
if(tmp_1 & (1<<i)){
if(tmp_2 & (1<<i)){
if(i==m-1) return false;
if(!(tmp_2 &(1<<(i+1)))) return false;
if(!(tmp_1 &(1<<(i+1)))) return false;
i+=2;
}
else{
i++;
}
}
else{
if(!(tmp_2 & (1<<i))){
return false;
}
else{
i++;
}
}
}
return true;
}
int main(){
while(~scanf("%d %d",&n,&m) && (n+m)){
if( (n*m)%2 ){
printf("0\n");
continue;
}
if(n<m){
int t=n;
n=m;
m=t;
}
memset(dp,0,sizeof(dp));
int Max=(1<<m);
for(int i=0;i<Max;i++){
if(judge(i))
dp[0][i]=1;
}
for(int i=1;i<n;i++){
for(int j=0;j<Max;j++){
for(int k=0;k<Max;k++){
if(ok(j,k)){
dp[i][j]+=dp[i-1][k];
}
}
}
}
printf("%I64d\n",dp[n-1][Max-1]);
}
}