2016.8.5_状压dp
来源:互联网 发布:c语言输出杨辉三角形 编辑:程序博客网 时间:2024/04/30 17:57
今天我们讲了状态压缩动态规划,主要就是将多维的状态用二进制压缩起来,降低代码复杂度和时间复杂度。
举些栗子:
1.POJ3254
在一个n*m(1<=n,m<=12)的田地中种玉米,有些格子不能放,要求不能有任何两个玉米相邻,问有多少种方法(方法数mod 100000000)。
看这题第一个想到的方法就是深搜,时间复杂度是O(2^n*m),也就是2^144,很明显是超时的。再看一遍题目,发现第i行的放置方法,只被第i-1行所影响,想到了动规的无后效性,所以可以用动规。
如果每一行有m个格子,那么第i行的状态可以表示为dp[i][F1][F2]…[Fm],但这样m不同时,数组维度也不同,不好写(也可以写12种dp,太麻烦)。因为这后面的状态都是01,所以就可以用一个二进制将后面所有状态压缩起来,这就实现了状态压缩。
枚举状态的时候,只要从0到2^m循环就可以了。其他操作可以用位运算来实现。
代码:
#include <iostream>#include <fstream>#include <string.h>using namespace std;const int MAXSTATE = 1<<12;int c=0,ans,n,m,fl;int state[MAXSTATE];int p[13][MAXSTATE];int f[13];void kick() //去掉一行中有相邻1的状态{ for (int i=0;i<MAXSTATE;i++) if ( ( i & (i<<1) ) == 0 ) state[c++]=i;}int main(){ cin >> n >> m; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { cin >> fl; f[i]+=fl<<(m-j); } kick(); p[0][0]=1; for (int i=1;i<=n;i++) //枚举行 { for (int s=0;s<c;s++) //枚举上一行的状态 { if ( (state[s] | f[i] )!=f[i] ) continue; //判断土地是否可以种 for (int s1=0;s1<c;s1++) //枚举当前行的状态 { if ( ((state[s1] | f[i-1] )==f[i-1]) && (state[s1] & state[s]) == 0 )//判断上一行的状态是否可以种在土地上,以及医这一行有没有相邻 p[i][s]=(p[i][s]+p[i-1][s1])%100000000;//累加方案数 } } } for (int s=0;s<c;s++) { ans=(ans+p[n][s])%100000000; //统计答案 } cout << ans << endl;}
2.POJ2411
给出一个n×m的棋盘,问用1×2的牌放满它(可以横放或竖放),共有有多少种方法。
暴力方法就不说了,直接讲正解,一行的状态同样可以用一个二进制数保存,同样是当前行的状态只与上一行有关,但当前行的状态可以通过上一行的状态和dfs求出。
但为了使全部填满,应该遵循以下规则:
当上一行状态为0时,上下放一个竖的,只有这样才能保证在跳到i+1行时上面都是满的。
当上一行状态为1时,有两种选择,第一种是不放(留给右边横放或下一行竖放),第二种是与左边一格一起横放一个(前提是左边的是空的)。
代码:
#include <iostream>#include <fstream>#include <string.h>using namespace std;const int MAXSTATE = 1<<11;int n,m;long long p[13][MAXSTATE];void dfs(int s,int s1,int i,int k)//搜索当前行的状态{ if (k>=m) //搜索完成 { p[i][s]+=p[i-1][s1]; return ; } if ( (s1&(1<<k))!=0 ) { dfs(s,s1,i,k+1); if ( k!=0 && ( s & (1<<(k-1)) ) == 0 ) dfs( (s|(1<<k)|(1<<(k-1))) , s1 , i , k+1 ); } else dfs( ( s | (1<<k) ) , s1 , i , k+1 );}int main(){ cin >> n >> m; while (n!=0 && m!=0) { memset(p,0,sizeof p); p[0][MAXSTATE-1]=1; dfs(0,MAXSTATE-1,1,0); for (int i=2;i<=n;i++) { for (int s1=0;s1<MAXSTATE;s1++) { if (p[i-1][s1]==0) continue; dfs(0,s1,i,0); // cerr << p[i-1][s1]; } } cout << p[n][(1<<m)-1] << endl; cin >> n >> m; }}
1 0
- 2016.8.5_状压dp
- POJ1185_炮兵阵地_状压dp
- 邦德_纪中1236_最大权匹配_状压dp
- Dp&&背包_模板
- HDU_4352 _ XHXJ's LIS (数位Dp+LIS + 状压)
- 动态规划_树形DP
- 动态规划_数位DP
- ZJOI2008_骑士_基环树&&dp
- 动态规划_区间DP
- 2011 Asia Dalian Regional Contest _ Rescue the Rabbit AC自动机+状压DP
- 环中环_纪中1347_线段树+dp
- 引水入城_洛谷1514_搜索 + dp
- jzoj 1154_购物_最大流/树形dp
- 动态规划_复杂状态DP
- 057_完全背包问题(DP)
- HDU2089_不要62_数位DP
- 2017.10.22_数位dp模板分析
- 专题(弱点)Dp训练总结【状压Dp*1+区间Dp*5+数位dp*3+树型Dp*2】【10/11】
- POJ:2367 Genealogical tree(普通简单版拓扑排序)
- 五大常用算法之五:分支限界法(转)
- Reward(反向拓扑)
- 正对android面试的一些技术问题(摘抄自网络)
- 机器学习简单实验(线性分类)
- 2016.8.5_状压dp
- Gradle介绍
- HDU 1058 Humble Numbers (思维)
- linux kernel file_open
- 位运算入门应用以及技巧
- artist-mode & ditaa
- PyQt5教程(四)——事件与信号
- 【解题报告】2016.8.5·Day2·状压DP
- 【CodeForces】701A - Cards(暴力 || 思维)