哈尔滨理工大学软件与微电子学院第八届程序设计竞赛同步赛(高年级)B 小乐乐搭积木 (状态压缩)
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
64bit IO Format: %lld
题目描述
小乐乐想要给自己搭建一个积木城堡。
积木城堡我们假设为n*m的平面矩形。
小乐乐现在手里有1*2,2*1两种地砖。
小乐乐想知道自己有多少种组合方案。
输入描述:
第一行输入整数n,m。(1<=n,m<=10)
输出描述:
输出组合方案数。
示例2
题目大意:
给你一块n*m的方块,以及若干1*2的小木板,问你有多少种方式能够使小木板填满方块。
状压DP模板题。和POJ2411雷同。
摘自focus_best的csdn博客:
首先我们定义如下这种填充表示方式:如果一个骨牌是横着放的,那么它所在的两个方格都填充1.如果它是竖着放的,那么它所在的两个格子中,上面的那个填0,下面的这个填1.由此可以得到断言:该矩阵的骨牌摆放方法和该矩阵的二进制表示法是一一对应的。
现在我们专注于这个问题:如何求相邻两行二进制值的对应关系?可以枚举i-1行的所有二进制值情况,然后判断这个值本身是否合法,如果合法(其实只要这行是中间行所有二进制值都合法的,因为首行我们虚构了第0行为全1序列,最后一行我们只需要全1序列对应的值),再通过它推断出和它兼容的第i行二进制值(和i-1行二进制值兼容的第i行二进制为:第i-1行为0的位,第i行应为1。第i-1行为1的位,第i行为0或1,且如果第i行为1那么表示第i行此时的对应位置是横着放的,应该有偶数个连续的1才合法,只需判断i-1行中为1的位在第i行中如果也为1必须偶数个这样的1相连)。
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cmath> #include <queue> #include <deque> #include <stack> #include <map> #include <set> typedef long long ll; const int mod=1000000007; const int inf=1000000000; const int maxn=10; const int maxm=200; int n,m; int dp[maxn+5][2<<(maxn+5)]; int main() { scanf("%d%d",&n,&m); memset(dp,0,sizeof(dp)); dp[0][(2<<(m-1))-1]=1; for(int i=1; i<=n; i++) { for(int k=0; k<=((2<<(m-1))-1); k++)//i行 { if(i==n&&k<((2<<(m-1))-1)) continue; for(int j=0; j<=((2<<(m-1))-1); j++)//i-1行 { bool flag=true; for(int p=0,cnt=0; p<=m-1; p++) { if((j&(1<<p))==0) { if(cnt%2) flag=false; else cnt=0; if((k&(1<<p))==0) flag=false; } if((j&(1<<p))>0) { if((k&(1<<p))>0) cnt++; else { if(cnt%2) flag=false; else cnt=0; } } if(p==m-1) { if(cnt%2) flag=false; } if(!flag) break; } if(flag) { dp[i][k]+=dp[i-1][j]; } } } } printf("%d\n",dp[n][((2<<(m-1))-1)]); return 0; }