状态压缩DP解POJ3254-Corn Fields
2016-02-03 12:32
323 查看
本文内容转自:http://blog.csdn.net/harrypoirot/article/details/23163485?utm_source=tuicool&utm_medium=referral
对状态压缩DP有了初步的认识,自己做个记录,方便以后回忆时查看。有些自己当时不太好理解的地方加上注释。想从0开始看的童鞋先看上面的链接。
先贴代码,这是poj上的一道题的题解:http://poj.org/problem?id=3254
先把自己的几点理解写在这里,大家可以先看下面代码,不明白的地方再翻回来看。
1. 状态数组state长度我设为378。是因为题目中要求N<=12,则令N=12,调用init()函数一算就可知top=377,最多有378个状态数,并且我把下标都改为从0开始了,强迫症啊。
2. 关于statte数组,当有N列数时(全是0或1,当成二进制数看),最大就是N个1,也就是(1<<N) - 1。这些个数中,有很多是相邻位都为1的,要剔除,比如3(11),state数组只保存了合符要求的。
3. cur[i]保存的为什么是第i行的逆,而不直接保存原数。cur表示的是实际的01数,state保存的是所有可能取的数。现在就是要判断每一个可能取的合理的数,是否满足当前牧场块分布(cur值)的要求。比如state取101时,如果人家牧场cur是110,肯定不行。二者怎么进行比较判断呢?直接相与,等于0肯定不可行,但不等于0不一定可行。这里有个逻辑就是cur必须把state的所有1覆盖(可以多出),才是可行的。如果将cur取逆,则cur为1的位是不可行的,相与等于1说明将1放在不该放的地方,肯定不可行;整个值相与为000说明不该放的没放,可以放的放不放无所谓了,都行的。这种做法很巧妙,背后的数学原理是什么,是什么逻辑运算?麻烦知道的告诉我。
4. 后面没什么好说的了。填dp[i][k]的时候是三层循环而不是二层。对于第i行来说,第k个状态取值不仅要和当前实际值cur[i]比较,比较可行后还要和上一行所有状态比较呢。
对状态压缩DP有了初步的认识,自己做个记录,方便以后回忆时查看。有些自己当时不太好理解的地方加上注释。想从0开始看的童鞋先看上面的链接。
先贴代码,这是poj上的一道题的题解:http://poj.org/problem?id=3254
先把自己的几点理解写在这里,大家可以先看下面代码,不明白的地方再翻回来看。
1. 状态数组state长度我设为378。是因为题目中要求N<=12,则令N=12,调用init()函数一算就可知top=377,最多有378个状态数,并且我把下标都改为从0开始了,强迫症啊。
2. 关于statte数组,当有N列数时(全是0或1,当成二进制数看),最大就是N个1,也就是(1<<N) - 1。这些个数中,有很多是相邻位都为1的,要剔除,比如3(11),state数组只保存了合符要求的。
3. cur[i]保存的为什么是第i行的逆,而不直接保存原数。cur表示的是实际的01数,state保存的是所有可能取的数。现在就是要判断每一个可能取的合理的数,是否满足当前牧场块分布(cur值)的要求。比如state取101时,如果人家牧场cur是110,肯定不行。二者怎么进行比较判断呢?直接相与,等于0肯定不可行,但不等于0不一定可行。这里有个逻辑就是cur必须把state的所有1覆盖(可以多出),才是可行的。如果将cur取逆,则cur为1的位是不可行的,相与等于1说明将1放在不该放的地方,肯定不可行;整个值相与为000说明不该放的没放,可以放的放不放无所谓了,都行的。这种做法很巧妙,背后的数学原理是什么,是什么逻辑运算?麻烦知道的告诉我。
4. 后面没什么好说的了。填dp[i][k]的时候是三层循环而不是二层。对于第i行来说,第k个状态取值不仅要和当前实际值cur[i]比较,比较可行后还要和上一行所有状态比较呢。
#include <cstdio> #include <cstring> using namespace std; #define mod 100000000 int M, N, top = 0; //top表示每行最多的状态数 int state[378]; //state存放每行所有的可行状态(即没有相邻的状态 int dp[20][378]; //dp[i][j]:对于前i行数据,每行有前j种可能状态时的解 int cur[20]; //cur[i]表示的是第i行整行的情况 inline bool ok(int x) { //判断状态x是否可行 if (x&x << 1) return false;//若存在相邻两个格子都为1,则该状态不可行 return true; } void init() { //遍历所有可能的状态 top = 0; int total = 1 << N; //遍历状态的上界 for (int i = 0; i < total; ++i) { if (ok(i))state[top++] = i; } } inline bool fit(int x, int k) { //判断状态x 与第k行的实际状态的逆是否有‘重合’ if (x&cur[k])return false; //若有重合,(即x不符合要求) return true; //若没有,则可行 } /* 此处,注意要用相反存储的数据来判断, 因为若10101001是一种可行状态,则可知101001也可行(是前者的一部分) 这时x即为10101001,cur[k]为10110,x&cur[k]=0,即符合条件 */ int main() { freopen("in.txt", "r", stdin); while (scanf("%d%d", &M, &N) != EOF) { init(); memset(dp, 0, sizeof(dp)); for (int i = 0; i < M; ++i) { cur[i] = 0; int num; for (int j = 0; j < N; ++j) { //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位 scanf("%d", &num); //表示第i行第j列的情况(0或1) if (num == 0) //若该格为0 cur[i] += (1 << (N - j -1)); //则将该位置为1(注意要以相反方式存储,原因参看line34-36 } } for (int i = 0;i < top;i++) { if (!(state[i]&cur[0])) { //判断所有可能状态与第一行的实际状态的逆是否有重合 dp[0][i] = 1; //若第1行的状态与第i种可行状态吻合,则dp[0][i]记为1 } } /* 状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态) */ for (int i = 1; i < M; ++i) { //i索引第2行到第M行 for (int k = 0; k < top; ++k) { //该循环针对所有可能的状态,找出一组与第i行相符的state[k] if (!(state[k] & cur[i])) //某状态符合第i行实际情况 { for (int j = 0; j < top; ++j) { //找到state[k]后,再找一组与第i-1行符合,且与第i行(state[])不冲突的状态state[j] if (!(state[j] & cur[i - 1]) && !(state[k] & state[j])) //符合第i-1行实际,并且i-1行和i行不冲突 dp[i][k] = (dp[i][k] + dp[i - 1][j]) % mod; //上一行中状态'j'下的情况累加到本行状态‘k'上 } } } } int ans = 0; for (int i = 0; i < top; ++i) { //累加最后一行所有可能状态的值,即得最终结果! ans = (ans + dp[M-1][i]) % mod; } printf("%d\n", ans); } return 0; }2. POJ-1185,炮兵阵地。地址:http://poj.org/problem?id=1185 题解如下:
#include <iostream> #include <cstdio> using namespace std; const int N = 101; const int M=11; const int MAXS = 61; char matrix [M]; int dp [MAXS][MAXS]; int state[MAXS]; int num[MAXS]; int cur ; int top = 0, n = 0, m = 0; int getnum(int i) //求某状态二进制数1的个数 { int ret = 0; while(i) { i &= i - 1; ++ret; } return ret; } void initstate() { top = 0; int total = 1<<m; for(int i = 0; i < total; ++i) { if(!(i&(i<<1)) && !(i&(i<<2))) { state[top] = i; num[top++] = getnum(i); } } } int main() { freopen("in.txt", "r", stdin); int i = 0, j = 0, k = 0, t = 0; while(EOF != scanf("%d%d", &n, &m)) { for(i = 0; i < n; ++i) { cur[i] = 0; scanf("%s", matrix[i]); for(j = 0; j < m; ++j) { if('P' == matrix[i][j]) { cur[i] += (1<<(m-j-1)); } } } for(i = 0; i < n; ++i) for(j = 0; j < MAXS; ++j) for(k = 0; k < MAXS; ++k) dp[i][j][k] = -1; initstate(); for(i = 0; i < top; ++i) if(!(state[i]&(~cur[0]))) dp[0][i][0] = num[i]; //dp[i][j][k] = max{dp[i][j][k], dp[i-1[k][t]]+num[j]} for(i = 1; i < n; ++i) { for(j = 0; j < top; ++j)// state of row i { if((~cur[i])&state[j]) continue; for(k = 0; k < top; ++k)//state of row i-1 { if((~cur[i-1])&state[k]) continue; if(state[j]&state[k]) continue; for(t = 0; t < top; ++t)//state of row i-2 { if(state[j]&state[t]) //不用再判断state[k]与state[t]了,求i-1行时已经成立 continue; if(-1 == dp[i-1][k][t]) continue; dp[i][j][k]=max(dp[i][j][k], dp[i-1][k][t]+num[j]); } } } } int ret = 0; for(i = 0; i < top; ++i) { for(j = 0; j < top; ++j) { if(dp[n-1][i][j]>ret) ret = dp[n-1][i][j]; } } printf("%d\n", ret); } return 0; }
相关文章推荐
- Python实现浏览器自动化操作
- 三星N900(note3)刷机包 颓废N0.8.1 修复已知BUG 集成谷歌服务
- 关于jsonp的理解
- atitit。自己定义uml MOF EMF体系eclipse emf 教程o7t
- YisouSpider你想搞死我的服务器吗?
- [LeetCode]217. Contains Duplicate
- Ninja介绍
- 测试-Unity组件的调用顺序
- Remove duplicates from sorted listed 2
- 类与类之间的关系
- socket connect的问题
- 选择安卓系统中的图片资源
- codevs 2806 红与黑
- 【bug】No package npm available.
- Collections.sort对List排序的两种方法
- Oc-语句总结(2)--NSArray(补充)—对象
- 有限自动机(finite automation)[转]
- C++中智能指针的设计和使用
- iOS微信第三方登录实现
- K Closest Points