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】

11111
11K4K3K2
K1K0now  
     

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

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值