题目描述

有一个m行n列的矩阵,用1*2的骨牌(可横放或竖放)完全覆盖,骨牌不能重叠,有多少种不同的覆盖的方法? 你只需要求出覆盖方法总数mod p的值即可。

输入格式

三个整数数n,m,p,m<=5,p<=10000,n<=10000

输出格式

一个整数:总数模p的结果


不难想到可以用状压来做这题。设dp(i,j)表示第i列放置情况为j的二进制表示,其中j的第k位为1时表示这玩意是一块竖着的骨牌的上半部分,为0则是其余的情况。我们考虑一下dp(i,j)可以由哪些状态转移而来。

设上一行的二进制表示为j,当前一行的为k。由于当j的某些位置为1时,k的这些位置也必须为1。为了在满足我们的定义的同时把j的1给转移下来,我们可以将j和k做一次按位或运算。此时数j|k中为0的部分就是放横着的骨牌的地方。显然j|k中为0的连续部分长度必须是偶数。所以我们转移的第一个条件就是:

1.j|k的每一段连续0的长度都必须为偶数

如果上一行的某一位是1,而当前一行的这一位也是1,那么不合法,不能转移。所以我们的第二个转移的条件就是:

2.j和k的相同位置不能都为1

怎么判断两个条件呢?

对于第二个条件,我们可以将j和k做一次按位与运算,如果得到的数不为0,即得到的数里面含有1,那么不合法:

if(j&k) continue;

对于第一个条件,我们只好O(m)地慢慢转移:

int odd=0,cnt=0;
for(register int l=0;l<m;l++)
if((j|k)>>l&1) odd|=cnt,cnt=0;
else cnt^=1;
if(odd|cnt) continue;

所以我们得到了一个时间复杂度为O(NM * 2^M * 2^M)=O(NM * 4^M)的算法。

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxm 5
#define maxn 10001
using namespace std; int dp[maxn][1<<maxm];
int n,m,p; inline int read(){
register int x(0),f(1); register char c(getchar());
while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
} int main(){
n=read(),m=read(),p=read();
dp[0][0]=1;
for(register int i=1;i<=n;i++){
for(register int j=0;j<1<<m;j++){
for(register int k=0;k<1<<m;k++){
if(j&k) continue;
int odd=0,cnt=0;
for(register int l=0;l<m;l++)
if((j|k)>>l&1) odd|=cnt,cnt=0;
else cnt^=1;
if(odd|cnt) continue;
(dp[i][j]+=dp[i-1][k])%=p;
}
}
}
printf("%d\n",dp[n][0]);
return 0;
}

这个复杂度足够通过本题了。


对于这个算法有个小小的优化:

设函数f(j,k)=j|k,不难发现其定义域大小为2M2=4M而值域大小只有2M,所以我们对于一个f(j,k)其实重复算了2^M次。所以我们可以预处理出所有f(j,k):

for(register int i=0;i<1<<m;i++){
int odd=0,cnt=0;
for(register int j=0;j<m;j++)
if(i>>j&1) odd|=cnt,cnt=0;
else cnt^=1;
even[i]=odd|cnt?0:1;
}

然后在dp的过程中:

dp[0][0]=1;
for(register int i=1;i<=n;i++){
for(register int j=0;j<1<<m;j++){
for(register int k=0;k<1<<m;k++){
if(!(j&k)&&even[j|k]) (dp[i][j]+=dp[i-1][k])%=p;
}
}
}

可以把时间复杂度优化成O(N * 4^M+M * 2^M)

最新文章

  1. php内核分析(八)-zend_compile
  2. ubuntu将命令写在一个文件里,执行文件,source命令
  3. 做自己的类库dll文件
  4. Linux 修改主机名 和 ip 映射关系
  5. iOS 单元测试之XCTest详解(一)
  6. lsslot
  7. 【Android测试】【第六节】Monkey——认识和使用
  8. Android Please ensure that adb is correctly located at问题解决
  9. 取得DisplayMerics手机屏幕大小的应用
  10. C# C/S 结构操作Ini系统文件
  11. (原)VS2013在Release情况下使用vector有时候会崩溃的一个可能原因
  12. zen cart global $db 这噶哒
  13. 责任链模式(Chain of Responsibility)
  14. Vue的土著指令和自定义指令
  15. 第一个spring简单的helloworld
  16. windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)
  17. C++常量 运算符
  18. 红黑树与AVL树
  19. office install problems
  20. 20145209刘一阳《JAVA程序设计》第四周课堂测试

热门文章

  1. NameVirtualHost *:80 has no VirtualHosts
  2. 安利一波这12个IDEA插件,太香了!
  3. 赶紧收藏!Spring MVC 万字长文笔记,我愿奉你为王者笔记!
  4. python干货:pop()函数的用法 [弹出删除功能]
  5. PHP代码样例
  6. ASP.NET Core 3.1使用Swagger API接口文档
  7. 简析5G时代的MART流处理
  8. 2.k8sPod、控制器、service
  9. 如何写出安全的、基本功能完善的Bash脚本
  10. 如何将离线计算业务的成本降低65%——弹性容器服务EKS「竞价实例」上线