poj3524 Corn Fields(状态压缩dp)

来源:互联网 发布:大数据涉及技术要求 编辑:程序博客网 时间:2024/05/16 09:12

前两天实验室没网,好好看了下状态压缩,现在编辑一下(2016/4/11)。

说真的,入门真的很难懂,难点有两个。位运算,dp关系的处理。


先说明什么是状态压缩:用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。 参考博客,真理get~


再声明各个数组的作用:

state[N],刚开始初始化状态,先对1左移宽度大小,然后从1开始记录每个状态。其中每个数字代表一个状态,存到state里。

cur[N],记录样例中每行的状态,如果是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[N], cur[20], dp[20][N];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);    }}


0 0
原创粉丝点击