poj3254-Corn Fields(浅谈及详谈状压dp)

前两天打了一场比赛,状压不会,再次发现知识短板,哭...

第一次学的时候是大一学的,但是失败了,没学会,后来心理阴影总感觉很难,就没再学过。

这两天又重新学了一下。

谈一谈入门题~poj3254

题意:有一个n*m的0-1矩阵草地,1代表在这里可以放牛,0代表不能放牛。每两头牛不能相邻(左右相邻或者上下相邻),问有多少种放牛的方法。

 


直接进入正题:

(以下皆为学习完状态压缩后的personal comprehension,如若不对,敬请大佬指出~)

简单的说,状态压缩就是把问题的状态用一个十进制表示。一般的,问题的状态只有两种,走或者不走,选或者不选,放或者不放,我们都可以用0和1来表示这两种状态,但是如果单单用0和1来表示问题所有的状态,那么所涉及到的情况会有很多,所以我们把它转化为十进制,并且用 按位与,按位或,异或,左移右移等这些操作,可以方便且快速的得到问题的状态和结果。


回到问题,对于样例:

1 1 1

0 1 0

我们发现,总共有三列,也就是说,对于每一行,假设这一行所有的草地都能放牛,即输入为1 1 1的时候(样例的第一行),

我们可以得到下面

0 0 0; 0 0 1;0 1 0;1 0 0;1 0 1 五种情况,它分别对应十进制 

   0           1         2         4          5

也就是说,对于m=3的时候,每行放牛的情况最多就只有 这五种,我们先不要去想整个矩阵,只需要单独考虑一行,因为我们可以在后续的操作中通过和上一行的状态比较,来删除掉一些不符合情况的状态。

即:对于输入的m最多有(2^m)- 1 种状态,这些状态是可以预处理出来的,只需要让两个1不相邻即可,如下:

void init()
{
    len=0;
    for(int i=0; i<(1<

 

我们已经分析完了第一行的状态,一共有五种。

那么我们看第二行的输入为 0 1 0,值为0的草地不能放牛,所以这一行只有两种状态,即:

0 0 0 ; 0 1 0,分别代表十进制

   0            2

这样,我们就可以拿这一行的每个状态和上一行的每个状态比较,去除掉上下相邻的情况,得到最终的答案。

 

dp[ i ] [ state[j] ] 为对于第 i 行,第state[j]中状态时,放牛的方案总数、

那么对于每一行的状态state[j],我们都可以从上一行所有的状态里得到。

即动态转移方程为:

dp[j][state[j]]=dp[i-1][state[1]]+dp[i-1][state[2]]+...+dp[state[len],其中len为所有状态总数。

当然,其中还有不满足条件的,我们还需要加判断(详见代码)

 

到这里,状态压缩的过程基本就算完了。

综上所述,对于这道题,解决问题有如下三步:

第一步:预处理所有可能的状态:

void init()
{
    len=0;
    for(int i=0; i<(1<

第二步:统计出每行所有的状态。

其实这道题,把1设为可放牛,0设为不可放牛会更好理解些,不过问题不大,只是状态的一个逆。

怎么得到这一行所有的状态呢?举个栗子:

要得到第二行的状态,即 0和2,现在我们知道所有的状态有0 1 2 4 5.

当我们输入到第二行的时候,如我们输入的是 0 1 0

我们可以看成是1 0 1,然后把这个数转为十进制->5

用5(1 0 1)&按位与(所有的状态),如果得到的结果为不为0,那么肯定不符合条件。

比如上面按位与之后

      0 0 0       0 0 1       0 1 0       1 0 0       1 0 1

&   1 0 1       1 0 1       1 0 1        1 0 1       1 0 1

=    0 0 0       0 0 1       0 0 0        1 0 0       1 0 1

我们只取结果为0的状态,即 0 0 0 和 0 1 0,就是上面我们所提到的符合条件的状态。

//输入处理
for(int i=1; i<=n; i++)
{
     row[i]=0;
     for(int j=1; j<=m; j++)
     {
         int x;
         scanf("%d",&x);
         if(!x)
             row[i]+=1<<(m-j);
      }
}

第三步:预处理第一行的方案数

这个就不多说了

for(int i=0; i

第四步:去除掉不满足条件的状态,统计对于当前状态所有的方案数。

鉴于题目中要求上下也不能相邻,所以我们还需要加一个判断条件来去掉上下相邻的情况,这个很简单,只需要按位与上两个状态,结果为0就符合条件,否则不符合。

 

if(state[k]&state[j])
    continue;//不符合条件

 

所以总的代码如下:

#include
#include
#include
#include
#include
#define max(a,b)   (a>b?a:b)
#define min(a,b)   (a

 

 

写了好久,洗澡睡觉~

你可能感兴趣的:(动态规划/递推)