第九届蓝桥杯B组:搭积木题解-二维前缀和优化dp

小明对搭积木非常感兴趣。他的积木都是同样大小的正立方体。

在搭积木时,小明选取 m 块积木作为地基,将他们在桌子上一字排开,中间不留空隙,并称其为第0层。

随后,小明可以在上面摆放第1层,第2层,……,最多摆放至第n层。摆放积木必须遵循三条规则:

规则1:每块积木必须紧挨着放置在某一块积木的正上方,与其下一层的积木对齐;

规则2:同一层中的积木必须连续摆放,中间不能留有空隙;

规则3:小明不喜欢的位置不能放置积木。

其中,小明不喜欢的位置都被标在了图纸上。图纸共有n行,从下至上的每一行分别对应积木的第1层至第n层。每一行都有m个字符,字符可能是‘.’或‘X’,其中‘X’表示这个位置是小明不喜欢的。

现在,小明想要知道,共有多少种放置积木的方案。他找到了参加蓝桥杯的你来帮他计算这个答案。

由于这个答案可能很大,你只需要回答这个答案对1000000007(十亿零七)取模后的结果。

注意:地基上什么都不放,也算作是方案之一种。

【数据规模约定】

对于10%的数据,n=1,m<=30;

对于40%的数据,n<=10,m<=30;

对于100%的数据,n<=100,m<=100。

资源约定:

峰值内存消耗(含虚拟机) < 256M

CPU消耗  < 1000ms


思路:从暴力到暴力dp到优化dp

两个月前在写题解的时候并不会d这道题,这两天看了一下独自写出来了。

其实刚才搜了一下,题解挺多的。能写出这道题的dp也算学的不错了。感觉可能都太大佬了,并不屑于将这道题彻底将清楚,觉得太简单了吧。所以特此写一篇博客。一是记录自己的思维过程,一是希望能够帮助到更多人。

  ①暴力算n = 1 的情况,先拿到10%的分再说.

    起初时,我看这个题,跟两个月之前几乎一样没什么思路。但是心里想着这是OI赛制,再怎么说也要拿到暴力分吧?所以开始思考n = 1 ,只有一行的情况。根据题目所给的条件,我们知道,一行内这些个方块只能连续放置。那么怎么暴力计算呢。反正m很小,我们可以枚举连续的区间的长度len,从1 ~ n。然后对于所有可能的位置,check是否合法,合法的话答案就++.

    例如: n = 1 , m = 3. mp= {..x}

那么len = 1 的时候,有两个位置可以放, len = 2 ,只有一个位置可以放。len = 3的时候,没有位置可以放。

    ②发现暴力dp!

    现在,将其拓展成 n > 1的情况,显然,现在多了一个限制,如果本行要放区间为[L,R].那对上一行有一个限制,就是上一行所放的积木的区间[A,B] 一定要完全包含[L,R].  而且进一步可以发现,本行怎么放,只与它下面那一行有关系!所以我们自然先到了dp!

    状态就很好设计了:

这里注意,一定要把状态转移用严格的数学公式写出来,才能方便之后的优化.

现在我们可以用暴力的dp将答案计算出来。复杂度是多少呢?

dp状态解空间大小为:n * m * m , 而对于每一个状态,其决策有m * m 中,那么总复杂度为:O(n * m^4),只能过60%数据.

③怎么优化dp?

俗话说,dp就是填表的过程,我们试图将一个状态的决策展开成一张表。那么这张表有几个维度呢?

从状态定义就可以看出,显然是二维.现在看看这个转移实际上在做一个什么事情.(其实这一步优化的思维过程也可以从状态转移方程直接看出来)


我们惊奇的发现,它实质上就是将[l,r]这个位置的点的右上角的所有dp值给累加起来了,那么我们可以在进内层循环之前先对d(i - 1) 这个二维数组做一个前缀和,然后对于每一个点,就是对前缀和的一个差分,决策个数从O(m^2)直接降到了O(1)的查表.

④最后,dp的时候记得考虑判断某个格子能不能放,别整忘了.


代码就不放了,分析到这里,有dp基础的总能够写出来的.完全不会dp的看了代码可能也看不懂.

你可能感兴趣的:(第九届蓝桥杯B组:搭积木题解-二维前缀和优化dp)