HDU4804(插头dp)

这道题的题目大意是这样的:

有一个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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值