hiho 9 状态压缩二

来源:互联网 发布:网络销售微信聊天 编辑:程序博客网 时间:2024/06/05 00:29

问题描述

历经千辛万苦,小Hi和小Ho终于到达了举办美食节的城市!虽然人山人海,但小Hi和小Ho仍然抑制不住兴奋之情,他们放下行李便投入到了美食节的活动当中。美食节的各个摊位上各自有着非常多的有意思的小游戏,其中一个便是这样子的:

小Hi和小Ho领到了一个大小为N*M的长方形盘子,他们可以用这个盒子来装一些大小为2*1的蛋糕。但是根据要求,他们一定要将这个盘子装的满满的,一点缝隙也不能留下来,才能够将这些蛋糕带走。

这么简单的问题自然难不倒小Hi和小Ho,于是他们很快的就拿着蛋糕离开了~

但小Ho却不只满足于此,于是他提出了一个问题——他们有多少种方案来装满这个N*M的盘子呢?

值得注意的是,这个长方形盘子的上下左右是有区别的,如在N=4, M=3的时候,下面的两种方案被视为不同的两种方案哦!

解决方法

动态规划的本质是枚举,只是将枚举的中间结果记录下来,从而减少枚举的次数。另外,动态规划一般解决多阶段决策问题,首先应该搞清楚问题的阶段,例如01背包,对每个物体决定拿或不拿就是一个决策阶段,然后从最后一个决策阶段出发,即在最后一个物品,决定拿或不拿的时候,如果拿,那么我们需要知道dp(i-1, x-ci)是多少,如果不拿我们需要知道dp(i-1, x)是多少,然后才能通过计算知道拿或者不拿,这样的思考过程中我们就得到了需要记录的状态。同时也得到了状态转移公式,一般dp问题都是反着想,然后正着算。
对于这个问题,我们首先想到最笨的枚举方法:从左上角开始,从左到右,从上到下依次将所有的格子摆上蛋糕,注意到每个格子处可以选择横着摆或者竖着摆,所以每个格子就是一个阶段,而且前面格子的摆放方式对后面格子有影响,但是每次摆放只会影响到自己所在行和下一行,如果现在我们在第一个格子处横着摆,然后将前两行的状态记录下来,再递归求解在此状态下后面的可能性;如果再在第一个格子处竖着摆,记录下前两行的状态,再递归求解之后的状态数,至此将两种情况的个数相加就可以得到总个数了。所以我们的状态变量为dp(i, j, p1,p2..pm,q1,q2,...qm) 表示第i行之前都已放满,第i,和第i+1行分别按照p1 pmq1 qm的方式摆放,之后会有多少种摆放方式。状态转移就是在保证填满且不重复摆放的前提下,对i,j处的摆放进行枚举,然后求和,下面的程序使用记忆化搜索的方式,使得程序简练。
状态压缩就是p,q使用二进制表示。

#include <cstdio>#include <cstring>#include <algorithm>enum {mod = 1000000007, maxn = 1000+4, maxm2 = 1<<5, maxm = 5};int dp[maxn][maxm][maxm2][maxm2];int n, m;int f(int i, int j, int cur, int next){    if (i == n)//        return 1;    if (dp[i][j][cur][next] >= 0)        return dp[i][j][cur][next];    if (cur & (1<<j)){        if (j+1 < m)        {            return dp[i][j][cur][next] = f(i, j+1, cur, next);        }else        {            return dp[i][j][cur][next] = f(i+1, 0, next, 0);        }    }else{        dp[i][j][cur][next]= 0;        if (j+1 < m && !(cur&(1<<(j+1))))        {            dp[i][j][cur][next]+= f(i, j, cur|(1<<j)|(1<<(j+1)), next);            dp[i][j][cur][next]%=mod;        }        if (i+1<n && !(next&(1<<j)))        {            dp[i][j][cur][next]+= f(i, j, cur|(1<<j), next|(1<<j));            dp[i][j][cur][next]%=mod;        }        return dp[i][j][cur][next];    }}int main(){    //freopen("in.txt", "r", stdin);    scanf("%d %d", &n, &m);    memset(dp, -1, sizeof(dp));    printf("%d\n", f(0, 0, 0, 0));}
1 0