[USACO06NOV]玉米田Corn Fields——状态压缩dp

题目

题干

  农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列 (1 ≤ M ≤ 12 ; 1 ≤ N ≤ 12 ,每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?
输入输出格式


输入格式:
第一行:两个整数M和N,用空格隔开。第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。


输出格式:
一个整数,即牧场分配总方案数除以100000000的余数。


输入样例#1:
2 3
1 1 1
0 1 0
输出样例#1:
9

分析

有一个m乘n的矩阵,每一个方格是一个牧场,每一个牧场适合种玉米的用1表示,不适合的用0表示。这样做就记录了“障碍”

注意,一头牛都不放亦是一种方案,不要漏项。

我们目前有N列,也就是说,每一行里有N个方格。例如N为3,使用二进制数字表示,则一行里所有可能的状态有(先不考虑障碍问题):

000

001

010

011

100

101

110

111

一共有如上七种状态。明显的,在有N个格子的一行中,状态数目为2^N-1种,正好对应十进制数字0,1,2,3,4……2^N-1

现在,我们要考虑各种障碍问题。由于每一行的状态只和上一行有关

我们有三种障碍需要考虑:

1,玉米地本身是否适合。

2,同一行内左右不能有相邻的两个“1”

3,本行与上一行不可以有上下相邻的两个“1”

我们依次考虑。

1,玉米地本身是否适合。

  刚刚我们说了,每一个方格适合种玉米的用1表示,不适合的用0表示。我们使用a数组表示这个障碍。a [ x ]表示第x行的障碍情况。

  比如当前我们正在处理第 i 行,在第 i 行中枚举所有可能的状态,其状态对应的十进制数字是 j ,那么就有如下要求

  j==( a [ i ] & j );

  因为a数组里“1”表示的是“可以放置牛”,所以说,如果 a [ i ] & j == j 了,那么意思就是j 中的所有需要的“1”,a [ i ]中都有。

  如果等号不成立,那么就是a [ i ] 中有某些位置是0,而j中需要1,这样做显然是不可以的。

2,同一行内左右不能有相邻的两个“1”

  我们把 j 左移一位,再与 j 本身 作与运算,必须满足该运算的结果是 0 ,才符合要求。即:

  j & ( j << 1 ) == 0 ;

  举例子:

  j          001000101000100100   (没有相邻的1)

  j<<1    010001010001001000 

  结果:   000000000000000000   符合要求

 

  j         0010001100111000010    (有相邻的1)

  j<<1   0100011001110000100

  结果   0000001000110000000    不符合要求

  结合例子,我们可以玄学理解:当没有相邻的“1”的时候,这个数列就像一个“钉耙”一样,向左移动一位之后,很明显是“钉耙齿 对着 钉耙缝”,与运算后就全都归0

                当有相邻的“1”的时候,这个数列左移,00110与01100就会在第三位上产生1,就不符合。

3,本行与上一行不可以有上下相邻的两个“1”

  刚刚讲到,我们用 i 表示处理的阶段:即我们现在呆在第 i 行。 i 行现在的状态是 j

  我们现在再用k,来表示i-1行(上一行)的所有状态。

  当 j & k ==0 很显然是合法的。如果非0,那么j和k肯定在某位置上都是1,不满足上下不可以有相邻“1”的条件。

  注意,我们自上而下考虑,所以不关 i + 1 行, i + 2 行……后面那些行的事情

  而且依题意,本行的状态明显只与上一行的状态有关联。所以只考虑i与i-1的关系,而不去考虑 i 与 i - 2,i - 3 … …

  

如此,障碍分析完毕。动态转移方程自然就有

定义 f [ i ] [ j ] 为:前 i 行中 (且第 i 行 的状态 j 为合法的时候) 所有放牧的方案数目。

转移方程: f [ i ] [ j ] += f [ i - 1 ] [ k ] ,(k就是刚刚分析中那个k,即  ( j & k )  ==0 的那个k)

最后统计答案的时候,要注意ans把 f [ N ] [ j ] 所有 j 状态 都加上,再模上余数(当然模的操作在转移的时候就应当操作,否则可能会炸)

代码

 

 

当然,它还没有完。

这样做显然时间效率极其低下,为所以为了节省空间与时间,进行如下优化:

 

1,由于障碍的存在,导致状态 j 中本身就有许多许多不合法的。我们可以预处理,将所有不合法的 j 剔除,将所有合法状态 j 的代表十进制数字储存在一个新容器内,使用的时候调用所有容器内合法的状态就可以了。

2,由于每一行状态仅仅与上一行状态相关,所以可以使用两行滚动数组压维。

我们使用b数组记录所有合法状态。

 

 

祝AC

 

posted @ 2020-04-16 10:01  Dertangch  阅读(226)  评论(0编辑  收藏  举报