0403. 青蛙过河
大约 4 分钟
0403. 青蛙过河
- 标签:数组、动态规划
- 难度:困难
题目链接
题目大意
描述:一只青蛙要过河,这条河被等分为若干个单元格,每一个单元格内可能放油一块石子(也可能没有)。青蛙只能跳到有石子的单元格内,不能跳到没有石子的单元格内。
现在给定一个严格按照升序排序的数组 ,其中 代表第 块石子所在的单元格序号。默认第 块石子序号为 (即 )。
开始时,青蛙默认站在序号为 石子上(即 ),并且假定它第 步只能跳跃 个单位(即只能从序号为 的单元格跳到序号为 的单元格)。
如果青蛙在上一步向前跳跃了 个单位,则下一步只能向前跳跃 、 或者 个单位。
要求:判断青蛙能否成功过河(即能否在最后一步跳到最后一块石子上)。如果能,则返回 True
;否则,则返回 False
。
说明:
- 。
- 。
- 。
- 按严格升序排列。
示例:
- 示例 1:
输入:stones = [0,1,3,5,6,8,12,17]
输出:true
解释:青蛙可以成功过河,按照如下方案跳跃:跳 1 个单位到第 2 块石子, 然后跳 2 个单位到第 3 块石子, 接着 跳 2 个单位到第 4 块石子, 然后跳 3 个单位到第 6 块石子, 跳 4 个单位到第 7 块石子, 最后,跳 5 个单位到第 8 个石子(即最后一块石子)。
解题思路
思路 1:动态规划
题目中说:如果青蛙在上一步向前跳跃了 个单位,则下一步只能向前跳跃 、 或者 个单位。则下一步的状态可以由 种状态转移而来。
- 上一步所在石子到下一步所在石头的距离为 。
- 上一步所在石子到下一步所在石头的距离为 。
- 上一步所在石子到下一步所在石头的距离为 。
则我们可以通过石子块数,跳跃距离来进行阶段划分和定义状态,以及推导状态转移方程。
1. 划分阶段
按照石子块数进行阶段划分。
2. 定义状态
定义状态 表示为:青蛙能否以长度为 的距离,到达第 块石子。
3. 状态转移方程
- 外层循环遍历每一块石子 ,对于每一块石子 ,使用内层循环遍历石子 之前所有的石子 。
- 并计算出上一步所在石子 到当前所在石子 之间的距离为 。
- 如果上一步所在石子 通过上上一步以长度为 、 或者 的距离到达石子 ,那么当前步所在石子也可以通过 的距离到达石子 。即通过检查 、、 中是否至少有一个为真,即可判断 是否为真。
- 即:$dp[i][k] = dp[j][k - 1] \text{ or } dp[j][k] or dp[j][k + 1] $。
4. 初始条件
刚开始青蛙站在序号为 石子上(即 ),肯定能以长度为 的距离,到达第 块石子,即 。
5. 最终结果
根据我们之前定义的状态, 表示为:青蛙能否以长度为 的距离,到达第 块石子。则如果 为真,则说明青蛙能成功过河(即能在最后一步跳到最后一块石子上);否则则说明青蛙不能成功过河。
思路 1:动态规划代码
class Solution:
def canCross(self, stones: List[int]) -> bool:
size = len(stones)
stone_dict = dict()
for i in range(size):
stone_dict[stones[i]] = i
dp = [[False for _ in range(size + 1)] for _ in range(size)]
dp[0][0] = True
for i in range(1, size):
for j in range(i):
k = stones[i] - stones[j]
if k <= 0 or k > j + 1:
continue
dp[i][k] = dp[j][k - 1] or dp[j][k] or dp[j][k + 1]
if dp[size - 1][k]:
return True
return False
思路 1:复杂度分析
- 时间复杂度:。两重循环遍历的时间复杂度是 ,所以总的时间复杂度为 。
- 空间复杂度:。用到了二维数组保存状态,所以总体空间复杂度为 。