您的位置:首页 > 其它

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方法,例如炮兵阵地,切记。

觉得经典所以写了解释,但感觉写了这么多也只是入了个门吧,泪。

#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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj