【DP】2023Q2-超级玛丽过吊桥
题目描述与示例
题目描述
超级玛丽好不容易来到新的一关。有一个长长的吊桥共有 N
个木板,从吊桥一段的外侧开始跳(第 0
块),每一次可跳 1
、2
、3
步,其中有一些木板是陷阱,踩到即消耗一点生命值并在陷阱原地复活,刚好跳到吊桥的另一侧(第 N+1
块)则通关。 给定起始生命数量 M
,吊桥长度 N
,陷阱木板数量 K
及 K
个陷阱木板的编号,求保证生命值大于 0
条件下所有可能的通关路线数量。
输入描述
超级玛丽当前生命数:1 <= M <= 5
吊桥的长度:1 <= N <= 32
陷阱木板数:1 <= K <= 32
陷阱木板编号数组: L
是长度及元素不大于 N
的编号数组
输入结构
M N K` `L
提示
- 输入总是合法,忽略参数校验。
- 必须从起点开始走。
- 必须离开吊桥走到终点。
输出描述
输出通过此关的吊桥走法个数,如果不能通过此关,请输出 0
示例一
输入
2 2 1
2
输出
4
说明
2
个生命,2
个木板,缺失 1
个木板,第 2
个木板有缺失,一共有 4
种走法:
3
1
2
2
1
1
1
(复活)1
示例二
输入
1 3 2
1 3
输出
1
说明
1
个生命,3
个木板,缺失 2
个木板,第 1
、3
个木板有缺失,只有 1
种走法,其他走法都不能通关。
- 先走一步,死亡;
- 先走三步,死亡。
示例三
输入
3 10 24
4 7
输出
504
解题思路
如果本题没有剩余生命值和陷阱木板的限制,那么这题会是一道非常简单的题目,可以看作是泰波那契版本的LC70. 爬楼梯。但加上限制之后,题目变得复杂了很多。
这是一个典型的序列dp问题。我们考虑动态规划三部曲:
dp
数组的含义是什么?
对于dp[i]
,不仅要记录到达第i
块木板能够到达的方法数,还要记录到达该块木板时的剩余生命值。因此dp[i]
可以存入一个哈希表,其中key
为剩余生命值rest_life
,value
为以rset_life
到达位置i的方法数。
- 动态转移方程是什么?
跳跃到第i
块木板的方法数,为跳到第i-1
、i-2
、i-3
块木板的方法数的总和。另外,还需要考虑第i
块木板是否是陷阱木板。若
- 第
i
块木板不是陷阱木板,那么不会损失生命,故存在dp[i][rest_life] += dp[j][rest_life]
- 第
i
块木板是陷阱木板,那么会损失生命。故存在dp[i][rest_life-1] += dp[j][rest_life]
。注意必须满足rest_life-1 > 0
,这是因为如果跳跃到该陷阱木板后,剩余生命值降为0
,那么不能考虑该跳跃。
for i in range(1, N+2):
for j in range(max(0, i-3), i):
for rest_life in dp[j]:
if i not in L:
dp[i][rest_life] += dp[j][rest_life]
else:
if rest_life-1 > 0:
dp[i][rest_life-1] += dp[j][rest_life]
dp
数组如何初始化?
起点只有一种情况,即以初始生命值M
出现在起点。故以如下方式进行初始化
dp = [defaultdict(int) for _ in range(N+2)]
dp[0] = {M: 1}
代码
from collections import defaultdict
# 初始生命值M,吊桥长度N,陷阱木板的个数K
M, N, K = map(int, input().split())
# 陷阱木板的编号集合
L = set(map(int, input().split()))
# 初始化dp数组,dp[i]是一个哈希表,其中的
# key为剩余生命值rest_life,
# value为以rest_life到达吊桥位置i的方式数
dp = [defaultdict(int) for _ in range(N+2)]
# 初始化起点,只有一种情况
# 即以初始生命值M出现在起点
dp[0] = {M: 1}
# 从1开始,遍历包括终点N+1在内的所有位置
for i in range(1, N+2):
# 考虑i的前三个位置,即i-1,i-2和i-3
for j in range(max(0, i-3), i):
# 遍历dp[j]哈希表中的所有剩余生命值rest_life
for rest_life in dp[j]:
# 第i块木板不是陷阱木板,走到i仍剩余生命值是rest_life
if i not in L:
# 更新dp[i][rest_life]
dp[i][rest_life] += dp[j][rest_life]
# 第i块木板是陷阱木板,走到i是剩余生命值是rest_life-1
else:
# 如果剩余生命值仍然大于0
if rest_life-1 > 0:
# 更新dp[i][rest_life-1]
dp[i][rest_life-1] += dp[j][rest_life]
# dp结束后,dp[N+1]哈希表中的所有value求和,即为到达终点的方式数
print(sum(dp[-1].values()))
时空复杂度
时间复杂度:O(NM)
。对整个吊桥仅需做一次遍历,但每一个位置i
的动态转移过程都要遍历之前三个位置的哈希表,哈希表最大长度为初始生命值M
,即储存了剩余生命值为1
至M
的方法数目。
空间复杂度:O(NM)
。dp数组所占据的空间。
华为OD算法/大厂面试高频题算法练习冲刺训练
-
华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
-
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
-
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
-
60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁
-
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
-
可查看链接 OD算法冲刺训练课程表 & OD真题汇总(持续更新)
-
绿色聊天软件戳
od1336
了解更多