Campus Design(hdu 4804 轮廓线dp)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luyehao1/article/details/83507454

题目链接:

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;
}

猜你喜欢

转载自blog.csdn.net/luyehao1/article/details/83507454
今日推荐