这道题的题目大意是这样的:
有一个100*10的棋盘,上面有一些格子不能放砖块。
现在有两种砖块,1*1的和1*2的。
1*1的砖块最少放c个,最多放d个;1*2的砖块没有个数限制,可以水平放,也可以垂直放。砖块与砖块不能重叠。
问有多少种方案把整个棋盘放满。
解法:
用插头dp解决这类问题,用一个二进制数表示轮廓线上的插头状态,0表示没有插头,1表示有插头。
因为1*1的砖块有个限制,所以状态中再加一维表示1*1的砖块个数。
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define mod 1000000007
using namespace std;
int n,m,c,d;
char mp[110][12];
long long dp[2][12][2100][22];
void work() {
int all=1<<(m+1);
for (int i=0; i<=1; i++)
for (int j=1; j<=m; j++)
for (int k=0; k<all; k++)
for (int l=0; l<=d; l++)
dp[i][j][k][l]=0;
dp[0][m][0][0]=1;
for (int ii=1; ii<=n; ii++) {
int i=ii%2;
for (int j=1;j<=m;j++)
for (int k=0;k<all;k++)
for (int l=0;l<=d;l++)
dp[i][j][k][l]=0;
for (int k=0; k<(all>>1); k++)
for (int l=0; l<=d; l++)
dp[i][0][k<<1][l]=dp[(i+1)%2][m][k][l];
for (int j=1; j<=m; j++) {
int x=1<<(j-1);
int y=1<<j;
for (int k=0; k<all; k++){
for (int l=0; l<=d; l++) {
if (mp[ii][j]=='1') {
if ((x&k)&&(y&k)) dp[i][j][k][l]=0; //有两个插头
else if ((x&k)||(y&k)) {
int z=x|y;
dp[i][j][k][l]=dp[i][j-1][k&(~z)][l]; //只有一个插头
} else {
if (l>0) dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][j-1][k][l-1])%mod; //当前格子放1*1的
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][j-1][k|x][l])%mod;
dp[i][j][k][l]=(dp[i][j][k][l]+dp[i][j-1][k|y][l])%mod;
}
} else {
if ((k&x)||(k&y)) dp[i][j][k][l]=0;
else dp[i][j][k][l]=dp[i][j-1][k][l];
}
}
}
}
}
}
int main() {
while (~scanf("%d%d%d%d",&n,&m,&c,&d)) {
for (int i=1; i<=n; i++) scanf("%s",mp[i]+1);
work();
long long ans=0;
for (int i=c; i<=d; i++) ans=(ans+dp[n%2][m][0][i])%mod;
printf("%I64d\n",ans);
}
return 0;
}