POJ 2411 + POJ 2663 + POJ 3420 小方格填充之多米诺骨牌系列(状压DP)
来源:互联网 发布:男生双肩背包推荐 知乎 编辑:程序博客网 时间:2024/04/30 00:57
原文链接:http://blog.csdn.net/shiwei408/article/details/8821853
解决此类问题,一般都是用1*2的小方块填充 n*m的矩阵。其中n或m两者中至少有一个较小。
解决此类的方法,当n和m都很小的时候,可以直接for循环枚举状态来更新求值。但是当二者间有一个稍大的时候,就需要构造矩阵来解决此类问题。通常将较小的那个数作为列用dfs来枚举两行之间的状态。
状态标记: 横放和竖放的下一个均为1,竖放的上一个和不放置为0 ,每行可以转化为1个2进制数。当这一行访问结束时,就会得到上一行状态,和该行状态,因为所有情况都是我们设置的,所以pre状态一定会转化为now状态。
对于每一个位置,我们有三种放置方法:1. 竖直放置2. 水平放置3. 不放置。
dfs的时候三个参数分别是 num,now,pre,分别表示当前的列数,当前行的状态,上一行的状态,初始化的时候num=now=pre = 0。每两行之间有三种状态:
1. num = num + 1, now << 1 | 1, pre << 1; // 竖直放置,当前行该列为1,上一行该列置为为0
2. num = num + 2, (now << 2) | 3, (pre<< 2) | 3; // 横放 都为11
3. num = num + 1, now << 1, pre<< 1 | 1; // 上一行该列置为1,不能竖放,不放置的状态
注意不存在上一行当前列不放置,这一行放置的情况。
DFS枚举状态时候的情况:
typedef long long LL;int n,m,tan;LL dp[13][1<<11];LL matrix[11*(1<<12)][2];//注意是 11*(1<<12)void dfs(int num,int now,int pre){ if(num>m) return; if( num==m ) { matrix[++tan][0] = pre; matrix[tan][1] = now; return; } dfs(num+1,(now<<1)|1,pre<<1); dfs(num+2,(now<<2)|3,(pre<<2)|3); dfs(num+1,pre<<1,(now<<1)|1); return;}
更新答案的时候,最后的总ans就是dp[n][(1<< m)-1]的值。
memset(dp,0,sizeof(dp)); dp[0][(1<<m)-1] = 1; for(int i=1;i<=n;i++) { for(int j=1;j<=tan;j++) { dp[i][matrix[j][1]] += dp[i-1][matrix[j][0]]; } } cout<<dp[n][(1<<m)-1]<<endl;
POJ2411,n,m不大于11,小范围可递推,可用矩阵枚举。
#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>#include <climits>using namespace std;typedef long long LL;int n,m,tan;LL dp[13][1<<11];LL matrix[11*(1<<12)][2];void dfs(int num,int now,int pre){ if(num>m) return; if( num==m ) { matrix[++tan][0] = pre; matrix[tan][1] = now; return; } dfs(num+1,(now<<1)|1,pre<<1); dfs(num+2,(now<<2)|3,(pre<<2)|3); dfs(num+1,now<<1,(pre<<1)|1); return;}int main(){ while ( scanf("%d%d",&n,&m)!=EOF && n && m) { if( n<m ) swap(n,m); tan = 0; dfs(0,0,0); memset(dp,0,sizeof(dp)); dp[0][(1<<m)-1] = 1; for(int i=1;i<=n;i++) { for(int j=1;j<=tan;j++) { dp[i][matrix[j][1]] += dp[i-1][matrix[j][0]]; } } cout<<dp[n][(1<<m)-1]<<endl; } return 0;}
POJ 26633*12矩阵,代码与上类似,不再赘述。
POJ34201*2的小矩形,去拼接一个4*n(n<10^9)的矩形。N这么大递推肯定是不行了,所以我们要用矩阵快速幂进行加速,这个转移矩阵如何构造呢,我们可以直接用pre状态转移到now状态采用邻接矩阵的方式表述。
#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>#include <climits>using namespace std;typedef long long LL;int n,mod;struct mat{ LL at[16][16];};mat dat;void dfs(int num,int now,int pre){ if(num>4) return; if( num==4 ) { ++dat.at[pre][now]; return; } dfs(num+1,(now<<1)|1,pre<<1); dfs(num+2,(now<<2)|3,(pre<<2)|3); dfs(num+1,now<<1,(pre<<1)|1); return;}mat quick_pow(mat a,mat b){ mat tmp; memset(tmp.at,0,sizeof(tmp.at)); for(int i=0;i<16;i++) { for(int k=0;k<16;k++) { if( a.at[i][k]) for(int j = 0;j<16;j++) { tmp.at[i][j] = ( tmp.at[i][j] +(a.at[i][k] * b.at[k][j])%mod) %mod; } } } return tmp;}mat cal(mat a,int k){ if(k==1) return a; mat tmp; memset(tmp.at,0,sizeof(tmp.at)); for(int i=0;i<16;i++) tmp.at[i][i] = 1; if(k==0) return tmp; while(k) { if( k&1 ) tmp = quick_pow(a,tmp); k>>=1; a = quick_pow( a,a); } return tmp;}int main(){ memset(dat.at,0,sizeof(dat.at)); dfs(0,0,0); while ( scanf("%d%d",&n,&mod)!=EOF && n && mod) { if(mod==1) { cout<<0<<endl; continue; } mat ans = cal(dat,n); cout<<ans.at[15][15]<<endl; } return 0;}
- POJ 2411 + POJ 2663 + POJ 3420 小方格填充之多米诺骨牌系列(状压DP)
- 状态压缩DP poj 2663 骨牌铺方格
- poj 2411 骨牌覆盖问题 状压dp
- poj 2663 Tri Tiling dp(矩形填充)
- poj 3420 dp+矩阵(矩形填充)
- poj 2506 Tiling && nyoj 429 骨牌铺方格
- 铺瓷砖问题 HDU 2046 骨牌铺方格 + POJ 2663 Tri Tiling (递推)
- POJ 2411-Mondriaan's Dream(状压DP->骨牌铺满问题)
- POJ 2411 大矩形用1X2小矩形填充 状态dp DFS
- POJ 2411 大矩形用1X2小矩形填充 状态dp DFS
- POJ 2247 小DP?
- POJ 2411-状压dp
- POJ 2411 Mondriaan's Dream dp(矩形填充)
- poj 1717 背包dp(翻转骨牌的最少次数)
- poj 2747: 数字方格
- POJ 2411 状压DP&&DFS
- poj 2663 压缩DP
- poj 2411| 插头dp
- Problem C: 小数计算——结构体
- Bootst 按钮
- Site
- 2016SDAU课程练习一1004 E
- Android 开发百度地图之四 Poi
- POJ 2411 + POJ 2663 + POJ 3420 小方格填充之多米诺骨牌系列(状压DP)
- LTE attach 流程UE-eNodeB
- Problem D: 分数减法——结构体
- C2N4
- (深搜)HDU 1016-Prime Ring Problem
- 对象生命周期与可终结对象
- 用javascript模拟用户发起浏览器请求
- tcp 服务端FIN_WAIT_2 状态下,客户端操作的抛错实验
- HDU 1257 最少拦截系统【贪心】【DP】