POJ_2411_Mondriaan's Dream【狀態壓縮DP】

/*
Mondriaan's Dream
Time Limit: 3000MS           Memory Limit: 65536K
Total Submissions: 14596           Accepted: 8431

Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.

Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!

Input
The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.

Output
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.

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


問題描述:

給出一個n*m的棋盤,及一個小的矩形1*2,問用這個小的矩形將這個大的棋盤覆蓋有多少種方法。

由於我們放置小矩形的時候可以橫着放也可以豎着放,那麼就會產生不同的方法,但是必須滿足不產生空的未覆蓋的空格子。

首先我們用二進制1 0表示在某一個問題放置或者不放置,那麼有m列的棋盤,每一行有2*m個狀態了,我們現在就找出每行之間的規律。

對於一個矩形有3種方法:橫放,豎放,不放。由於第i行只跟第i-1行的放置有關係,因爲我們必須保證第i-1行使放滿的,現在用dp[i][state]表示第i行

狀態爲state的方法,那麼dp[i][curstate]=sum{dp[i-1][prestate]}.

1 橫放

如果第i行第d列我們選擇橫放,那麼第i行的第d列及d+1列都是1了,第i-1行第d列及d+1列也都必須爲1(保證是滿的),及狀態轉移爲:

d=d+2,curstate=curstate<<2|3,prestate=prestate<<2|3.

2豎放

第i行第d列我們選擇豎放,那麼第i行第d列爲1,第i-1行d列必須是0,(因爲我們是豎着放的,如果前一行不是空的如何能放下呢),狀態轉移:

d=d+1,curstate=curstate<<1|1,prestate=prestate<<1.

3不放

第i行第d列不妨,那麼第i-1行d列肯定是1,(保證是滿的),狀態轉移:

d=d+1,curstate=curstate<<1,prestate=prestate<<1|1.

這個題目採用記憶化搜索,對已經計算出的狀態值方法記錄,還有就是初始化的時候將dp[0][2<<m-1]=1,這樣第0行使放滿的,就不用單獨進行初始化了(單獨初始化的時候,由於是第一行,不存在豎着放的可能)。

我們的目標就是求dp[n][2<<m-1]了, 源碼如下:

*/

#include <stdio.h>  
#include <string.h>  
int row, col;  
long long dp[12][1<<11];  
void dfs(int r, int c, int pre, int now)  
{  
    if(c == col)  
    {  
        dp[r][now] += dp[r-1][pre];  
        return;  
    }  
    if(c + 1 <= col)  
    {  
        dfs(r, c + 1, pre<<1, (now<<1)|1);  //不放
        dfs(r, c + 1, (pre<<1)|1, now<<1);  //豎放
    }  
    if(c + 2 <= col)                        // 橫放
    {  
        dfs(r, c + 2, (pre<<2)|3, (now<<2)|3);  //左移兩位,相當於成 4 用 3 | 來判讀 當位放 1 還是 0 
    }  
}  
int main()  
{  
    while(scanf("%d%d", &row, &col), row!=0&&col!=0)  
    {  
        if((row * col) % 2 == 1)  
        {  
            printf("0\n");  
            continue;  
        }  
        int t ;
        if(row < col)//行列變化,讓列是小的值,因爲行是線性的,而列的可能值是指數的  
        {  
        	t = row;
        	row = col;
        	col = t;
            //row ^= col;  col ^= row;  row ^= col;  
        }  
        memset(dp, 0, sizeof(dp));  
        dp[0][(1<<col) - 1] = 1;  
        for(int i = 1; i <= row; i++)  
            dfs(i, 0, 0, 0);  
        printf("%lld\n", dp[row][(1<<col) - 1]);  
    }  
    return 0;  
}  


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