题目大意

用1x2的单元拼接出 NxM的矩形,单元可以横放或者纵放,N < 1000, M <= 5. 求不同的拼接方案总数。

分析

计算机解决问题的基本思路:搜索状态空间。如果采用dfs进行搜索,则可以将当前搜索的位置(i, j)作为状态,而不保存棋盘的占用情况,每次扩展时进行判断是否可行。这样可以得到最后的结果,但当然会超时。 
    (1) 参考hiho_1048中的提示,可以知道在搜索的时候按照固定的顺序可以大幅度剪枝(通过将无序的搜索变成有序的搜索来实现)。然后可以对中间结果进行保存,即记忆化搜索。状态dp[i][j][s1][s2] 表示求第i行,第j列位置开始扩展,且第i行的占用状态为s1, 第i+1行的占用状态为s2 时,填满剩余的空格的方案总数。

(2)将记忆化搜索直接使用动态规划来实现,状态 dp2[i][k1][k2] 表示前i-1行都已经填满了,此时第i行的占用情况的二进制表示转换为十进制为 k1,第i+1行的占用情况的二进制表示转换为十进制为 k2,达到此时棋盘占用情况的摆放方案总数。 
    这样,可以进行状态转移时,枚举第i行的占用情况k1,和第i+1行的占用情况k2,看是否可以进行扩展,blabla...

实现

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define BIT(i, j) (i & (1 << (j - 1)))
const int mod = ;
int dp[][][][]; /*记忆化搜索
n,m为边界n行,m列。
求第i行,第j列位置开始扩展,且第i行的占用状态为s1, 第i+1行的占用状态为s2 时,填满剩余的空格的方案总数
*/
int dfs(int n, int m, int i, int j, int s1, int s2){
if (dp[i][j][s1][s2] != -)
return dp[i][j][s1][s2];
int result = ;
//边界!! 无法继续扩展时,取值为1,表示所有扩展到此的合理情况的初始均为1
if (i > n || j > m || s1 >= ( << m) || s2 >= ( << m))
return ;
//如果第i行位置j(从右向左)被占用,且j < m,向左扩展
if (BIT(s1, j) && j < m)
result = dfs(n, m, i, j + , s1, s2);
//如果第i行位置j(从右向左)被占用,且j = m,向下一行扩展
else if (BIT(s1, j) && j == m)
result = dfs(n, m, i + , , s2, );
//如果第i行位置j没有被占用,且无法向左或者向下扩展
else if (BIT(s1, j) == && (j == m || BIT(s1, j + )) && (i == n || BIT(s2, j)))
result = ;
//如果第i行位置j没有被占用,且只能向左扩展
else if (BIT(s1, j) == && j < m && BIT(s1, j + ) == && (i == n || BIT(s2, j)))
result = dfs(n, m, i, j, s1 | ( << (j - )) | ( << j), s2);
//如果第i行位置j没有被占用,且只能向下扩展
else if (BIT(s1, j) == && (j == m || BIT(s1, j + )) && (i < n && BIT(s2, j) == ))
result = dfs(n, m, i, j, s1 | ( << (j - )), s2 | ( << (j - )));
//如果第i行位置j没有被占用,且可以向左或向下扩展
else if (BIT(s1, j) == && j < m && BIT(s1, j + ) == && i < n && (BIT(s2, j) == ))
result = dfs(n, m, i, j, s1 | ( << (j - )) | ( << j), s2) +
dfs(n, m, i, j, s1 | ( << (j - )), s2 | ( << (j - )));
//记忆化保存
return dp[i][j][s1][s2] = result % mod;
} //动归数组, dp2[i][k1][k2] 表示前i-1行都已经填满了,此时第i行的占用情况的二进制表示转换为十进制为 k1,
// 第i+1行的占用情况的二进制表示转换为十进制为 k2
int dp2[][][];
int main(){
int n, m;
scanf("%d %d", &n, &m);
/*
记忆化搜索
memset(dp, -1, sizeof(dp));
int result = dfs(n, m, 1, 1, 0, 0);
printf("%d\n", result);
*/ //动态规划
memset(dp2, , sizeof(dp2));
//初始状态,第1行未被占用,只有1种可能。
dp2[][][] = ;
int mx = << m;
for (int i = ; i <= n; i++){
    /*
      下面的 j (第i行从右到左的每个位置)循环放在 k1(第i行当前的bit状态) 之前是为了去除重复! 因为我们总是按照顺序从上到下,从右到左枚举当前可以放置方块的位置点,
      在循环到每个(i, j)点,都看当前的哪些k1状态可以在次扩展
    */
for (int j = ; j <= m; j++){
//枚举第i行的所有情况(占用情况的二进制表示转换为十进制)
for (int k1 = ; k1 < mx; k1++){
//如果第i行的第j个位置(从右向左)为0,则可以从这里开始扩展
if ((k1 & ( << (j - ))) == ){
//看是否可以横向扩展
if (j < m && ( (( << j) & k1) == )){
//状态转移
for (int k2 = ; k2 < mx; k2++){
dp2[i][k1 | ( << j) | ( << (j - ))][k2] =
(dp2[i][k1 | ( << j) | ( << (j - ))][k2] + dp2[i][k1][k2]) % mod;
}
}
if (i < n) //看是否可以纵向扩展
//当前的第i+1行的占用情况
for (int k2 = ; k2 < mx; k2++){
//如果第i+1行的第j个位置(从右向左)为0,则可以纵向扩展
if ((( << (j-)) & k2) == )
//状态转移
dp2[i][k1 | ( << (j - ))][k2 | ( << (j - ))] =
(dp2[i][k1 | ( << (j - ))][k2 | ( << (j - ))] + dp2[i][k1][k2]) % mod;
}
}
}
}
//当第i行全部被占用满时,可以将第i+1行的状态 转移到 i+1行的当前行
for (int k = ; k < mx; k++){
dp2[i + ][k][] = dp2[i][mx - ][k];
}
}
//最后的结果是n行全部占满,此时第n行为 mx-1, 第n+1行为0
printf("%d\n", dp2[n][mx-][]);
return ;
}

hiho_1048_状态压缩2的更多相关文章

  1. POJ 3254. Corn Fields 状态压缩DP (入门级)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9806   Accepted: 5185 Descr ...

  2. HDU 3605:Escape(最大流+状态压缩)

    http://acm.hdu.edu.cn/showproblem.php?pid=3605 题意:有n个人要去到m个星球上,这n个人每个人对m个星球有一个选择,即愿不愿意去,"Y" ...

  3. [HDU 4336] Card Collector (状态压缩概率dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4336 题目大意:有n种卡片,需要吃零食收集,打开零食,出现第i种卡片的概率是p[i],也有可能不出现卡 ...

  4. HDU 4336 Card Collector (期望DP+状态压缩 或者 状态压缩+容斥)

    题意:有N(1<=N<=20)张卡片,每包中含有这些卡片的概率,每包至多一张卡片,可能没有卡片.求需要买多少包才能拿到所以的N张卡片,求次数的期望. 析:期望DP,是很容易看出来的,然后由 ...

  5. codeforces B - Preparing Olympiad(dfs或者状态压缩枚举)

    B. Preparing Olympiad You have n problems. You have estimated the difficulty of the i-th one as inte ...

  6. NOIP2005过河[DP 状态压缩]

    题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...

  7. vijos1426兴奋剂检查(多维费用的背包问题+状态压缩+hash)

    背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...

  8. hoj2662 状态压缩dp

    Pieces Assignment My Tags   (Edit)   Source : zhouguyue   Time limit : 1 sec   Memory limit : 64 M S ...

  9. poj3254 状态压缩dp

    题意:给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法.     分析:假如我们知道第 i-1 行的所有的可以放的情况,那么对于 ...

随机推荐

  1. Unity中内置Shader源码的获取方式

    现在可以直接在Unity下载页面获得 http://unity3d.com/get-unity/download/archive 包括StandardShader,StandardShaderGUI. ...

  2. view class source code with JAD plugin in Eclipse

    The default class viewer doesn't decompile the class file so you cannot open and check the source co ...

  3. nylg 小M的因子和

    小M的因子和 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 小M在上课时有些得意忘形,老师想出道题目难住他.小M听说是求因子和,还是非常得意,但是看完题目是求A的B ...

  4. java--4种内部类

    内部类: 一 非静态内部类 //非静态内部类 //非静态内部类可任意调用外部类的局部变量,无论是否private //在外部类中要实例化内部类:InnerClass inner = new Inner ...

  5. php获取本周和上周的开始日期和结束日期

    <?php header('Content-type: text/html; charset=utf-8'); $date=date('Y-m-d'); //当前日期 $first=1; //$ ...

  6. EF 增删改查

    StudentEntities entities = new StudentEntities(); //初始化对象 Register register = new Register(); #regio ...

  7. UIView如何管理它的子视图

    UIView提供了很多建立和管理视图的方法. 1.添加视图 insertSubview:atIndex:   //放在子视图数组的具体索引位置 insertSubview:aboveSubview:  ...

  8. Centos6.6上安装mysql5.6中的一些典型问题

    经过两天的摸索,终于成功在CentOS6.6系统上成功安装了mysql5.6,现整理如下. (1)安装时的问题: 最小化安装后,安装rpm包时经常会遇到 linux/centos Header V3 ...

  9. 四HttpServletResponse常见应用——生成验证码

    转载自http://www.cnblogs.com/xdp-gacl/p/3791993.html 一.HttpServletResponse常见应用——生成验证码 1.1.生成随机图片用作验证码 生 ...

  10. [SAP ABAP开发技术总结]DIALOG屏幕流逻辑

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...