请求解释解决Project Euler第115题的动态规划代码原理
拆解Project Euler第115题的动态规划解法
嘿,我来帮你把这段简洁的动态规划代码讲明白!先从核心的动态规划思路说起,再一步步对应到代码的每一行。
先明确定义:ways[k]的含义
代码里的ways[k]就对应题目中的填充计数函数F(m, k)——也就是长度为k的格子行,满足放置规则的合法填充方式总数。
初始化的逻辑
ways = [1]*(m) + [0]*(n-m+1)
当格子长度k < m时,我们根本放不下任何一个最小长度为m的红色方块,所以只有全黑这一种填充方式。因此前m个元素(对应k=0到k=m-1)都初始化为1;后面的位置先设为0,之后再逐步计算。
核心递推公式:ways[k] = ways[k-1] + sum(ways[:k-m]) + 1
这个公式是整个解法的灵魂,我们可以把ways[k](也就是F(m,k))拆成三种互不重叠的合法情况:
- 最后一个格子是黑色:这种情况的数量等于
ways[k-1]——因为前面k-1个格子的所有合法填充方式,只需要在末尾加一个黑格子就依然合法。 - 末尾放置红色方块,且方块前至少有一个黑格子:这部分对应的是
sum(ways[:k-m])。具体来说:- 假设红色方块长度为
t ≥ m,它占据了最后t个格子,那么方块的起始位置s必须满足k - s ≥ m(也就是s ≤ k - m)。 - 当
s > 0时,方块的前一个位置(s-1)必须是黑格子,所以前面s-1个格子的合法填充方式数就是ways[s-1]。把所有s从1到k-m的情况加起来,就是sum(ways[:k-m])。
- 假设红色方块长度为
- 整个格子行被一个红色方块填满:这是一种特殊情况(起始位置
s=0),没有前面的黑格子需要考虑,所以单独加1。
把这三种情况加起来,就得到了所有合法的填充方式总数。
举个小例子验证
比如m=3,k=3时:ways[3] = ways[2] + sum(ways[:0]) + 1 = 1 + 0 + 1 = 2
对应两种方式:全黑、全红,完全符合预期。
再看k=4:ways[4] = ways[3] + sum(ways[:1]) +1 = 2 + ways[0] +1 = 2+1+1=4
对应四种方式:全黑、红块在0-2+黑、红块在1-3+黑、全红,和实际情况一致。
为什么比暴力法高效?
暴力法需要枚举所有可能的红块位置组合,时间复杂度是指数级的,而动态规划通过递推一步步计算,时间复杂度是O(n²)(如果用前缀和优化还能降到O(n)),对于m=50、n=168这种规模,计算起来非常轻松。
内容的提问来源于stack exchange,提问作者Morten92
相关产品推荐
相关产品推荐

