poj3524 Corn Fields(状态压缩dp)
2016-04-09 20:40
344 查看
前两天实验室没网,好好看了下状态压缩,现在编辑一下(2016/4/11)。
说真的,入门真的很难懂,难点有两个。位运算,dp关系的处理。
先说明什么是状态压缩:用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。 参考博客,真理get~
再声明各个数组的作用:
state
,刚开始初始化状态,先对1左移宽度大小,然后从1开始记录每个状态。其中每个数字代表一个状态,存到state里。
cur
,记录样例中每行的状态,如果是0,通过将1左移相应位置来代表0(注意1变0,0变1,这样方便求&),同样转化为十进制,存到cur里。
两个数组都弄好后,初始化dp第一行,通过对state[j]和cur[i]求&,找出2^n所有状态中对于当前状态可行(没有1是连续)的状态,将其初始化为1(一种方案)。
接下来的dp都是建立在第一行的基础上,数塔式递归。先遍历每行每列,然后由于要和前一状态比较(纵向判断),再遍历一次每列。为了判断是否可行加以剪枝,一种是该可能状态与当前行状态判断,不匹配即跳过。另一种是与上一行判断,不匹配即跳过。注意这里比较的是整行的状态,而不是单个状态。
最后把最后一行全加起来,即为总方案数(题中要求取模别忘了)。
然后是难点:
1、位运算。这里涉及到的位运算有两个,<<(左移)和&(与)。左移太简单不说,主要是&运算。
首先明白&运算的过程:4=0000 0000 0000 0100 &7 =0000 0000 0000 0111= 0000 0000 0000 0100。说白了就是变为二进制后相同位置同时为1才能为1,否则为0。看似简单,但是可以做到我们好多想象不到的事。
(1)、本题中x & (x << 1)可以判断是否有1相邻,若相邻则求的一个不为0的数,返回false(因为相邻,所以该种情况不可行)。
(2)、本题中x & y可以判断是否冲突。
一种是state[j]&cur[i],因为本题cur中1即是0,所以一旦求的一个不为0的数,返回false(这个地方不能放玉米)。
一种是state[j] & state[k],即为上下行状态的比较,若相同位置同为1(都放玉米),求的不为0的数,跳过循环。
2、dp关系的处理。
并不是所有的状态压缩都可以像本题一样数塔式递归,不同的题用不同的dp方法,例如炮兵阵地,切记。
觉得经典所以写了解释,但感觉写了这么多也只是入了个门吧,泪。
说真的,入门真的很难懂,难点有两个。位运算,dp关系的处理。
先说明什么是状态压缩:用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。 参考博客,真理get~
再声明各个数组的作用:
state
,刚开始初始化状态,先对1左移宽度大小,然后从1开始记录每个状态。其中每个数字代表一个状态,存到state里。
cur
,记录样例中每行的状态,如果是0,通过将1左移相应位置来代表0(注意1变0,0变1,这样方便求&),同样转化为十进制,存到cur里。
两个数组都弄好后,初始化dp第一行,通过对state[j]和cur[i]求&,找出2^n所有状态中对于当前状态可行(没有1是连续)的状态,将其初始化为1(一种方案)。
接下来的dp都是建立在第一行的基础上,数塔式递归。先遍历每行每列,然后由于要和前一状态比较(纵向判断),再遍历一次每列。为了判断是否可行加以剪枝,一种是该可能状态与当前行状态判断,不匹配即跳过。另一种是与上一行判断,不匹配即跳过。注意这里比较的是整行的状态,而不是单个状态。
最后把最后一行全加起来,即为总方案数(题中要求取模别忘了)。
然后是难点:
1、位运算。这里涉及到的位运算有两个,<<(左移)和&(与)。左移太简单不说,主要是&运算。
首先明白&运算的过程:4=0000 0000 0000 0100 &7 =0000 0000 0000 0111= 0000 0000 0000 0100。说白了就是变为二进制后相同位置同时为1才能为1,否则为0。看似简单,但是可以做到我们好多想象不到的事。
(1)、本题中x & (x << 1)可以判断是否有1相邻,若相邻则求的一个不为0的数,返回false(因为相邻,所以该种情况不可行)。
(2)、本题中x & y可以判断是否冲突。
一种是state[j]&cur[i],因为本题cur中1即是0,所以一旦求的一个不为0的数,返回false(这个地方不能放玉米)。
一种是state[j] & state[k],即为上下行状态的比较,若相同位置同为1(都放玉米),求的不为0的数,跳过循环。
2、dp关系的处理。
并不是所有的状态压缩都可以像本题一样数塔式递归,不同的题用不同的dp方法,例如炮兵阵地,切记。
觉得经典所以写了解释,但感觉写了这么多也只是入了个门吧,泪。
#include <stdio.h> #include <algorithm> #include <string.h> using namespace std; const int N = 4500; const int INF = 1e8; const int mod = 100000000; int n, m, top, total; int state , cur[20], dp[20] ; bool ok(int x) { if(x & (x << 1)) return false; else return true; } void init() { top = 0; total = 1 << n; for(int i = 0; i < total; i ++) { if(ok(i)) state[++ top] = i; } } bool fit(int x, int y) { if(x & y) return false; else return true; } int main() { // freopen("in.txt", "r", stdin); int num; while(~scanf("%d%d", &m, &n)) { memset(dp, 0, sizeof(dp)); init(); //输入,同时记录该行的状态 for(int i = 1; i <= m; i ++) { cur[i] = 0; for(int j = 1; j <= n; j ++) { scanf("%d", &num); if(num == 0) cur[i] += (1 << (n - j)); } } //初始化第一行 for(int i = 1; i <= top; i ++) { if(fit(state[i], cur[1])) dp[1][i] = 1; } //分层dp for(int i = 2; i <= m; i ++) { for(int j = 1; j <= top; j ++) { if(!fit(state[j], cur[i])) continue; for(int k = 1; k <= top; k ++) { if(!fit(state[k], cur[i - 1])) continue; if(state[j] & state[k]) continue; dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod; } } } //输出 int ans = 0; for(int i = 1; i <= top; i ++) ans = (ans + dp[m][i]) % mod; printf("%d\n", ans); } }
相关文章推荐
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1001
- POJ ACM 1002
- 1611:The Suspects
- POJ1089 区间合并
- POJ 2635 The Embarrassed Cryptographe
- POJ 3292 Semi-prime H-numbers
- POJ 2773 HAPPY 2006
- POJ 3090 Visible Lattice Points
- POJ-2409-Let it Bead&&NYOJ-280-LK的项链
- POJ-1695-Magazine Delivery-dp
- POJ1523 SPF dfs
- POJ-1001 求高精度幂-大数乘法系列
- POJ-1003 Hangover
- POJ-1004 Financial Management
- 用单调栈解决最大连续矩形面积问题
- 2632 Crashing Robots的解决方法
- 1573 Robot Motion (简单题)
- POJ 1200 Crazy Search(简单哈希)
- 【高手回避】poj3268,一道很水的dijkstra算法题