题目链接:
题意:
有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;
}