POJ2411 - Mondriaan's Dream(状态压缩DP)

题目大意

给定一个N*M大小的地板,要求你用1*2大小的砖块把地板铺满,问你有多少种方案?

题解

刚开始时看的是挑战程序设计竞赛上的关于铺砖块问题的讲解,研究一两天楞是没明白它代码是怎么写的,智商捉急,上面是用逐格进行转移的,据说神马插头DP。。。坑爹啊。。。然后果断放弃研究了。。。我们还是逐行的进行转移,这样比较好理解,方程表示为:dp[i][j]+=dp[i-1][k](能够从上一行的状态k转移到当前状态j)。我们需要枚举出符合要求的状态j和k,如果第i-1行p列没有放,那么第i行的p列肯定需要放置一个竖块,如果第i-1行p列放了,那么i行的p列可以不用放,如果第i-1行的p列和p+1列都放了,那么我们可以在第i行的p列和p+1列横着放置一个砖块。

我们用dfs实现,那么上述三种情况可以分别表示为

dfs(step+1,s1<<1|1,s2<<1,line); 竖放
dfs(step+1,s1<<1,s2<<1|1,line);不放
dfs(step+2,s1<<2|3,s2<<2|3,line);横放

代码:

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

#define MAXN 15

typedef long long LL;

LL dp[MAXN][1<<MAXN];

int n,m;

void dfs(int step,int s1,int s2,int line)

{

    if(step==m)

    {

        dp[line][s1]+=dp[line-1][s2];

        return;

    }

    dfs(step+1,s1<<1|1,s2<<1,line);

    dfs(step+1,s1<<1,s2<<1|1,line);

    if(step+2<=m)

        dfs(step+2,s1<<2|3,s2<<2|3,line);

}

int main()

{

    while(scanf("%d%d",&n,&m)!=EOF&&(n+m))

    {

        memset(dp,0,sizeof(dp));

        dp[0][(1<<m)-1]=1;

        for(int i=1; i<=n; i++)

            dfs(0,0,0,i);

        printf("%I64d\n",dp[n][(1<<m)-1]);

    }

    return 0;

}

你可能感兴趣的:(poj)