poj 2411 骨牌覆盖问题 状压dp

博客详细介绍了POJ 2411题目的骨牌覆盖问题,利用状态dp的方法解决。关键在于理解每行状态必须有偶数个1相连,以确保完全覆盖前一行。状态表示为dp[state][i],表示第i行状态为state的方案数,转移方程是dp[state][i] += dp[state'][i-1],其中state'是不与i行冲突的状态。文章还提及使用dfs的解法可能更高效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:点击打开链接

题意:用1*2 的矩形通过组合拼成大矩形,求拼成指定的大矩形有几种拼法

分析:

这题的关键在于什么状态是合法的,可以这样想,用1表示有骨牌覆盖,用0表示空着。在覆盖第i行的时候,那么如果覆盖的状态是合法的,覆盖完后第i-1行必须没有空格了(全是1111),所以可以看出,每一行的状态必须要有偶数个1相连,为什么?

由于在做第i行dp时必须完全覆盖第i-1行,只要抓住这个条件不放就行。

1、如果第i行中有0,则第i-1行一定为1;

2、如果第i行中为1的x列第i-1行为0,说明第i行肯定是竖着放的;

3、如果第i行中为1的x列第i-1行的该列也为1,可能性只有一个,第i行是横放的,所以第i行的x+1列也必须为1,又因为第i行的x+1列为1是因为横着放的,所以第i-1行的x+1列也必须为1。

【状态表示】dp[state][i]第i行状态为state时候的方案数 

【转移方程】dp[state][i] += dp[state'][i-1] state'为i-1行的,不与i行状态state冲突的状态

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll f[1<<11][11];
bool ok[1<<11];
int n,m;
inline bool check(int x)
{
    int bit=0;
    while(x){
        if(x&1)bit++;
        else{
            if(bit&1)return false;
            bit=0;
        }
        x>>=1;
    }
    if(bit&1)return false;
    return true;
}
inline bool judge(int a,int b,int S)
{
    if((a|b)!=S-1)return false;
    return ok[a&b];
}
void init()
{
    memset(ok,0,sizeof(ok));
    for(int s=0;s<(1<<11);s++){
        if(check(s))ok[s]=1;
    }
}

int main()
{
    init();
    //freopen("f.txt","r",stdin);
    int S;
    while(~scanf("%d%d",&n,&m)&&(n+m)){
        if(m>n){
            int t=m;m=n;n=t;
        }
        S=1<<m;
        memset(f,0,sizeof(f));
        for(int s=0;s<S;s++){
            if(ok[s])f[s][0]=1;
        }
        for(int i=1;i<n;i++){
            for(int s=0;s<S;s++){
                for(int ss=0;ss<S;ss++){
                    if(judge(s,ss,S)){
                        f[s][i]+=f[ss][i-1];
                    }
                }
            }
        }
        printf("%lld\n",f[S-1][n-1]);
    }
    return 0;
}

还看到用人用dfs写的,在转移的时候逐行判断,好像更快点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值