返回

从递归到动态规划,走进算法之美——leetcode 2140 题解

后端

从递归到动态规划,走进算法之美——leetcode 2140 题解

算法之美,在于其简洁、高效,以及解决问题时思路的灵光乍现。leetcode 上的题目,正是一个个考验算法能力的练兵场。今天,我们就来共同探索 leetcode 第 2140 题的解题思路,从递归到动态规划,领略算法的魅力。

题目

给你一个整数数组 questions,其中 questions[i] 是你解决第 i 个问题需要花费的时间。同时给你一个整数 brainpower,是你的脑力值。每一分钟,你可以解决一个问题,每当你的脑力值低于或等于 0 时,你就会疲惫不堪并失去解决问题的动力。

你想要找到解决所有问题需要花费的 最短时间

示例:

输入:questions = [3,2,5,4,1], brainpower = 10
输出:7
解释:第一分钟解决第一个问题,脑力值变为 7。第二分钟解决第二个问题,脑力值变为 5。第三分钟解决第四个问题,脑力值变为 1。第四分钟解决第五个问题,脑力值变为 0。第五分钟解决第三个问题,脑力值变为 5。第六分钟和第七分钟分别解决第六个和第七个问题,脑力值变为 0。
因此,解决所有问题需要花费 7 分钟。

题解:

方法一:递归解法

递归的思路很简单,就是穷举所有可能的情况,直到找到最优解。具体步骤如下:

  1. 尝试解决第一个问题,此时脑力值变为 brainpower - questions[0]。
  2. 递归求解剩余问题,并将结果与当前解进行比较,取最优解。
  3. 回溯,尝试解决下一个问题,重复步骤 1 和 2。

这种方法看似可行,但遗憾的是,它在实际运行时会超时。究其原因,在于递归的计算过程过于冗余,重复计算了大量子问题。为了解决这个问题,我们需要引入一种更有效的方法——动态规划。

方法二:动态规划解法

动态规划是一种自底向上的求解方法,它通过存储子问题的解,避免重复计算。对于本题,我们可以定义一个二维数组 dp,其中 dp[i][j] 表示解决前 i 个问题,且脑力值为 j 时的最短时间。

def min_time(questions, brainpower):
  """
  :type questions: List[int]
  :type brainpower: int
  :rtype: int
  """
  n = len(questions)
  # 初始化 dp 数组
  dp = [[float('inf') for _ in range(brainpower + 1)] for _ in range(n + 1)]
  # 设置边界条件
  dp[0][brainpower] = 0

  # 迭代求解 dp 数组
  for i in range(1, n + 1):
    for j in range(brainpower, -1, -1):
      # 尝试解决当前问题
      if j >= questions[i - 1]:
        dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - questions[i - 1]] + 1)
      # 不解决当前问题
      else:
        dp[i][j] = dp[i - 1][j]

  # 返回最短时间
  return dp[n][brainpower]

时间复杂度:

动态规划的解法时间复杂度为 O(n * brainpower),其中 n 是问题数量,brainpower 是脑力值的上限。

总结:

动态规划是一种强大的算法技术,它可以有效地解决许多递归问题。leetcode 上的题目,为我们提供了绝佳的练习机会,让我们在实战中不断磨练算法技巧。希望本文对您有所帮助,也欢迎您在评论区留下您的想法和建议。