用1 ×2 的瓷磚覆蓋8 ×8 的地板,有多少種方式呢?

這個題在POJ上有,地址是:http://acm.pku.edu.cn/JudgeOnline/problem?id=2411
以前做過這個題,是用狀態DP來做的。大致說下方法,DP需要用到兩維,一維表示到達哪一行,另一維表示這一行的狀態。

用二進制來表示
假定方格爲N行M列。
表示方法如下:
f[i][j]。 ---- i表示第i行,j表示該行的二進制狀態。而f[i][j]則記錄由i,j構成的狀態有多少種擺法。

我舉個例子,假定總共有4行5列。
如下狀態: *號表示被鋪了方塊,0表示尚未被鋪方塊。
*****
*0**0
00000
00000
可以看到第2行的狀態爲*0**0,即用二進制來表示就是10110,即 f[2][22] 來表示。(當然,行號也可以從0開始計)

以上是狀態表示方法,有了狀態表示方法以後就需要得到一個進行狀態轉移的遞推式。
考慮i行j列的那個格子,有3種狀態,第一種是由第i-1行j列鋪上來,第二種是 在本行內,與相鄰的格子鋪的瓷磚,第三種該格子留空,以備下一行來使用。
枚舉這一行的格子的每一種狀態,即可以得到所需的上一行狀態。

舉例來說,枚舉一個如下狀態(*表示該格子是已經被填充的,-表示空格子,其餘數字均表示2*1的瓷磚擺法):

1***3**
1-22344

可知本行的狀態爲 1011111,生成該狀態所需要的上一行狀態爲,0111011。因爲只有這樣才能滿足瓷磚1和瓷磚3的嵌入且又不會留下空格子。

因此可以由本狀態進行一次累加:用二進制表示第二維 即 a[i][1011111] += a[i-1][0111011]。

那麼,最後 a[N][(1<<M)- 1]即爲所得。

POJ的這題的代碼(幾年前寫的,風格很爛,見笑了):
C/C++ code
#include <stdio.h> #include <string.h> long long s[14][14][5000]; int w,h; int list[20],v[20]; void GetList(int step){ if (step>w){ int value1=0; int value2=0; int i; for (i=1;i<=w;i++){ value1<<=1; value2<<=1; switch (list[i]){ case 0: value1|=1; break; case 1: value1|=1; value2|=1; break; case 2: value2|=1; break; } } if (h>1) s[w][h][value2]+=s[w][h-1][value1]; else s[w][h][value2]=1; return; } if (h>1) list[step]=2,GetList(step+1); if (step<w) list[step]=list[step+1]=1,GetList(step+2); list[step]=0; GetList(step+1); } int main(){ memset(s,0,sizeof s); int i,j,k; v[0]=1; for (i=1;i<20;i++) v[i]=v[i-1]<<1; for (w=1;w<=12;w++) for (h=1;h<=12;h++) GetList(1); for (scanf("%d%d",&w,&h);w;scanf("%d%d",&w,&h)) printf("%I64d\n",s[w][h][v[w]-1]); }


2953784 zyl072 2411 Accepted 7844K 124MS G++ 1222B 2007-11-29 10:22:08  

有點需要說明,由於這題的測試數據很多,因此我不是每次輸入後重新計算,而是選擇了一次性把所有可能輸入的結果全部算完。。然後再輸入後就可以直接輸出結果。這樣做在測試數據很多的情況下可以避免很多重複運算。因此我的DP狀態是用三維來表示的,分別是 行、列、當前行狀態。

轉載:http://topic.csdn.net/u/20100621/16/74D19159-8921-4405-8028-73B348755716.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章