1. 程式人生 > >Campus Design(hdu 4804 輪廓線dp)

Campus Design(hdu 4804 輪廓線dp)

題目連結:

Campus Design

 

題意:

有n*m個格子的矩形,有些格子不能放東西,其餘格子用1*2 、2*1 、1*1的木塊去填充,其中1*1的木塊的使用個數必須>=C且<=D,1*2和2*1的沒有限制,求能把矩形都填滿的方案數。

 

思路:

輪廓線DP模版(+解釋):【輪廓線DP】

1 1 1 1 1
1 1 K4 K3 K2
K1 K0 now    
         

now格點表示當前已經處理到的位置,由於只能用1*2 、2*1 、1*1的木塊,所以紅色的格點必須已經放有東西,因為在接下去的所有處理中紅色格點已經不會再被處理到了。而藍色格點表示還能被後續處理到。

 

因此,輪廓線狀壓的表示不是按照縱座標大小從左到右,而是按照從左到右,從上至下的順序(K4..K0)來的。

 

dp[2][D][1<<m]:當前位置的運算只與上一個位置有關,因此只需要存2個位置的狀態。第二維表示1*1的木塊的使用個數。第三維表示狀態。初始時(也就是在(0,0)位置計算之前),它的上一行不存在,我們按假設-1行都是1的狀態算(-1行都是已經放滿了的),且此時1*1使用0個,即 dp[0][0][(1<<m)-1]=1 。

 

相關位運算:(設現在輪廓線狀壓的狀態為k)

now的上方格點(K4)的取值: k&(1<<(m-1)) ;

now的左邊格點(K0)的取值: k&1

用2*1的木塊: 因為要豎著放,所以K4的取值必須為0,放好後的新狀態:舊狀態刪去首位,末尾為1

用1*2的木塊: 因為要橫著放,所以K0的取值必須為0且K4的取值必須為1,放好後的新狀態:舊狀態刪去首位,末尾2位為1

不放:K4的取值必須為1,放好後的新狀態:舊狀態刪去首位,末尾為0

用1*1的木塊:K4的取值必須為1,放好後的新狀態:舊狀態刪去首位,末尾為1,且1*1的使用次數++

 

code:

 

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 2e5+10;
const ll mod = 1e9+7;

int n,m;
char mp[105][15];
int C,D;
ll dp[2][25][1<<11];

int main()
{
    while(scanf("%d%d%d%d",&n,&m,&C,&D)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%s",mp[i]);
        memset(dp,0,sizeof(dp));
        int cur=0;
        dp[0][0][(1<<m)-1]=1;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                cur^=1;
                memset(dp[cur],0,sizeof(dp[cur]));
                if(mp[i][j]=='1'){
                    for(int t=0;t<=D;t++){
                        for(int k=0;k<(1<<m);k++){
                            //豎放
                            if(i!=0&&(k&(1<<(m-1)))==0){
                                int now = (((k<<1)|1)&((1<<m)-1));
                                dp[cur][t][now]+=dp[cur^1][t][k];
                                dp[cur][t][now]%=mod;
                            }
                            //橫放
                            if(j!=0&&(k&1)==0&&(k&(1<<(m-1)))){
                                int now = (((k<<1)|3)&((1<<m)-1));
                                dp[cur][t][now]+=dp[cur^1][t][k];
                                dp[cur][t][now]%=mod;
                            }
                            //不放
                            if(k&(1<<(m-1))){
                                int now = ((k<<1)&((1<<m)-1));
                                dp[cur][t][now]+=dp[cur^1][t][k];
                                dp[cur][t][now]%=mod;
                            }
                            //放1*1
                            if(k&(1<<(m-1))){
                                int now = (((k<<1)|1)&((1<<m)-1));
                                dp[cur][t+1][now]+=dp[cur^1][t][k];
                                dp[cur][t+1][now]%=mod;
                            }
                        }
                    }
                }
                else{
                    for(int t=0;t<=D;t++){
                        for(int k=0;k<(1<<m);k++){
                            //不能放相當於這個地方已經是1
                            if(k&(1<<(m-1))){
                                int now = (((k<<1)|1)&((1<<m)-1));
                                dp[cur][t][now]+=dp[cur^1][t][k];
                                dp[cur][t][now]%=mod;
                            }
                        }
                    }
                }
            }
        }
        ll ans=0;
        for(int i=C;i<=D;i++){
            ans+=dp[cur][i][(1<<m)-1];
            ans%=mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}