状压DP——hihoCoder 1048

  • 题目链接: https://hihocoder.com/problemset/problem/1048

  • 题意:给出一个N*M的矩形盒子,往里面放入1*2大小的长方形蛋糕,问将这个盒子填满有多少种放法

  • 分析:这还是一个通过记录搜索结果来进一步枚举状态的题,我们通过从上往下搜索每一行,从左往右搜索每一格,每次的状态定义为当前行和下一行的放置情况,若当前格子放有蛋糕,则标记为1,否则为0,所以每次枚举的状态是一个长度为2*M的二进制串,从低位到高位数,前M个位置为当前行,后M个位置为下一行。所以我们假设一个记录数组为DP[S]表示在放置某行某格时当前行和下一行状态为S时的方法数。

  • 转移方程:若在某次枚举状态 S0 中,当前行 i 的当前格 j 没有放置蛋糕,那么肯定可以竖着放下一个蛋糕,同时放置完这个蛋糕后的状态 S1 的数组 DP[S1]+=DP[S0] , 然后我们判断一下当前位置能否横着放下一个蛋糕,条件是 j+1格处为 0 且 j格不在这一行的最后一格,若满足则放置完这个蛋糕后的状态 S2 的数组 DP[S2]+=DP[S0]

  • 每次枚举完一行,我们需要还需要统计一下满足条件的状态数组,将它转移到下一行里去。即这些 S 有没有将当前行都放置满蛋糕(二进制串从右往左的M位是否都是1),若满足这个条件,则 DP[S]=DP[S] (S=(S>>M))

  • 最开始,我们需要把DP[0]设置为1,最后整个盒子放置满的方法数就是S为当前行全是1,下一行全是0的状态下数组里记录的结果了

  • AC代码:

/*************************************************************************
    > File Name: test.cpp
    > Author: Akira 
    > Mail: qaq.febr2.qaq@gmail.com 
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 100000
#define MaxM MaxN*10
#define INF 0x3f3f3f3f
#define bug cout<<88888888<<endl;
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
const int MOD = 1000000007;
template<typename _> inline void scan(_& t)
{
    int c;
    while((c = getchar()) < '0' || c > '9');
    t = c - '0';
    while((c = getchar()) >= '0' && c <= '9') t = t * 10 + c - '0';
}
template<typename _> inline void print(_ x)
{
    int len = 0, p[20];
    if(x < 0) putchar('-'), x = -x;
    while(x) p[++len] = x % 10, x /= 10;
    if(!len) p[++len] = 0;
    while(len) putchar(p[len--] + '0');
}

int N, M;
int DP[2][(1<<10)+1];

void solve()
{
    int l = (1<<M)-1;
    int len =  1 << (2*M);
    DP[0][0] = 1;
    int flag = 0;
    for(int i=0;i<N;i++)
    {
        CLR(DP[flag^1]);
        for(int j=0;j<M;j++)
        {
            for(int s=0;s<len;s++)
            {
                if( ((1<<j)&s)==0 ) 
                {
                    if( j!=M-1 && ((1<<(j+1))&s)==0  )
                        DP[flag][s|(1<<j)|(1<<(j+1))] = (DP[flag][s|(1<<j)|(1<<(j+1))] + DP[flag][s])%MOD;
                    DP[flag][s|(1<<(j+M))|(1<<j)] = (DP[flag][s|(1<<(j+M))|(1<<j)] + DP[flag][s])%MOD;
                }
            }
        }
        for(int s=0;s<len;s++) 
                if( (s&l) == l ) 
                    DP[flag^1][s>>M]=DP[flag][s];
        flag^=1;  
    }
    print(DP[flag^1][l]);
    cout << endl;
}
int main()
{
    scan(N);scan(M);
    solve();
    system("pause");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值