[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