【问题标题】:Why my DP algorithm has Time Limit Exceeded error为什么我的 DP 算法有 Time Limit Exceeded 错误
【发布时间】:2021-08-08 02:02:22
【问题描述】:

我是 DP 的初学者,我正在尝试解决一个 Leetcode 问题 - Maximum Subarray:给定一个整数数组 nums,找到总和最大的连续子数组(至少包含一个数字)并返回它的总和.我知道这个问题已经有很多 DP 解决方案。但是我想自己写一个DP解决方案,我在网上搜索了一些关于DP的介绍。这是我正在使用的具体参考:https://stackoverflow.com/a/13538715/9982458。根据这个答案,DP的运行时间是O(n)。为什么我的代码收到 Time Limit Exceeded 错误?任何 cmets 将不胜感激!谢谢!

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp = {} # dp[i] means maxSubArray for nums[0:i] which must has A[i-1] as the end element.
        m = nums[0]
        def DP(nums):
            if len(nums) in dp: 
                return dp[len(nums)]
            else:
                if len(nums) == 1: 
                    f = nums[-1]
                else:
                    temp = DP(nums[0:-1])
                    if temp > 0:
                        f = temp + nums[-1]
                    else:
                        f = nums[-1]
                dp[len(nums)] = f
                return f
        DP(nums)
        for i in range(1, len(nums)):
            m = max(m, dp[i])
        return m

【问题讨论】:

  • 动态规划方法的运行时间为O(n)是正确的。但是,您的实现是 O(n^2)。特别是你应该注意nums[0:-1] 不是一个常数时间的操作,所以你对DP 的调用是O(n)。这可以通过多种方式解决。
  • 你能说更多关于nums[0:-1] 不是常数时间吗?我不太明白我的nums[0:-1]stackoverflow.com/a/13538715/9982458 中的fib(n-1) + fib(n-2) 之间的区别。为什么这是一个恒定的时间?
  • 您的 DP 解决方案是自上而下的,但 nums[0:-1] 需要线性时间来切片阵列。尝试使用自下而上的 DP 解决方案
  • 这里的代码太多了。这最多应该是 5-6 行——如果你写的不止这些,可能会备份并重新评估。不需要任何切片。 nums[0:-1] 是列表的一个几乎完整的切片,逐个元素地复制它。如果列表有一百万个元素,则需要与此成正比的时间。 O(1) 意味着您可以在复制空列表的同时复制任意大的列表——显然是错误的。这与fib(n-1) + fib(n-2) 无关——如果没有更多上下文(没有记忆,O(2^n),有备忘录,O(n)),尚不清楚 TC 会是什么。
  • nums[0:-1] copies nums,所以它所花费的时间与它的长度成正比。如果你想在 Python 中为这个问题实现线性时间自上而下的 DP(我建议将其作为学习练习,因为我见过的所有实现都是自下而上的!),我认为你需要制作内部DP() 函数是一个不将nums 作为参数的闭包,而是依赖于它在其父函数范围内查看现有局部变量的能力。这个函数应该只接受一个整数endIndex 参数,可以在恒定时间内复制。

标签: algorithm time-complexity dynamic-programming divide-and-conquer


【解决方案1】:

现在 cmets 部分的人已经解释了为什么您的解决方案很慢,让我向您展示一个更简单的解决方案解决方案:

public int maxSubArray(int[] nums) {
    int max = Integer.MIN_VALUE;
    int maxEndingHere = 0;
    for (int value : nums) {
        maxEndingHere = Math.max(0, maxEndingHere) + value;
        max = Math.max(max, maxEndingHere);
    }
    return max;
}

它的工作原理是检查直到该点为止可能的最大和子数组。

【讨论】:

  • 是的,这个解决方案我很熟悉。我想知道你是否有一些线性时间 top-down DP 的实现来解决这个问题?
猜你喜欢
  • 2020-05-21
  • 2015-06-29
  • 2015-12-04
  • 1970-01-01
  • 2022-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多