POJ 2411 Mondriaan's Dream:網格密鋪類 狀壓dp

Leohh發表於2017-08-16

題目連結:http://poj.org/problem?id=2411

題意:

  給你一個n*m的網格 (1<=n,m<=11) ,往裡面鋪1*2或2*1的磚塊,問你鋪完這個網格有多少種不同的方法。

 

題解:

  表示狀態:

    dp[state][i] = num of ways at ith row

    (1)當前鋪到了第i行

    (2)在鋪第i行之前,第i行已經被佔的格子狀態為state

  如何轉移:

    對於當前第i行的第j列處,有三種情況:

    (1)豎著鋪。i+1行的第j個位置會被佔,在這一行佔用了一個寬度,接下來該在第j+1列鋪。

    (2)橫著鋪。對i+1行沒有影響,在這一行佔用了兩個寬度,接下來該在j+2列鋪。

    (3)已經被佔。只能不鋪,對i+1行沒有影響,接下來該在第j+1列鋪。

    所以在求dp之前先暴搜出在一行上的每個狀態state鋪完之後下一行的狀態,存到vector中。

    轉移:列舉每一行i,當前行的state,以及當前state能夠轉移的狀態nex。

      dp[nex][i+1] += dp[state][i]

 

AC Code:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <vector>
 5 #define MAX_N 15
 6 #define MAX_S (1<<12)
 7 
 8 using namespace std;
 9 
10 int n,m;
11 long long dp[MAX_S][MAX_N];
12 vector<int> shift[MAX_S];
13 
14 void dfs(int col,int state,int nex)
15 {
16     if(col==m)
17     {
18         shift[state].push_back(nex);
19         return;
20     }
21     if((state>>col)&1)
22     {
23         dfs(col+1,state,nex);
24         return;
25     }
26     dfs(col+1,state,nex|(1<<col));
27     if(col+1<m && !((state>>(col+1))&1)) dfs(col+2,state,nex);
28 }
29 
30 int main()
31 {
32     while(cin>>n>>m)
33     {
34         if(n==0 && m==0) break;
35         for(int state=0;state<(1<<m);state++)
36         {
37             shift[state].clear();
38         }
39         for(int state=0;state<(1<<m);state++)
40         {
41             dfs(0,state,0);
42         }
43         memset(dp,0,sizeof(dp));
44         dp[0][0]=1;
45         for(int i=0;i<n;i++)
46         {
47             for(int state=0;state<(1<<m);state++)
48             {
49                 if(dp[state][i])
50                 {
51                     for(int j=0;j<shift[state].size();j++)
52                     {
53                         int nex=shift[state][j];
54                         dp[nex][i+1]+=dp[state][i];
55                     }
56                 }
57             }
58         }
59         cout<<dp[0][n]<<endl;
60     }
61 }

 

相關文章