最大乘积区间问题 | 豆包MarsCode AI 刷题

69 阅读3分钟

这道题如果暴力来做,即枚举左端点和右端点,需要O(n2n^2)的复杂度。,而计算区间内元素相乘又需要O(n)的复杂度,因此总复杂度为O(n3n^3)。即使采用记录前缀乘积的方法,使计算复杂度变为O(1),总复杂度仍然有O(n2n^2)。下面介绍动态规划的做法,仅需遍历一次数组,复杂度为O(n)。

首先介绍一下动态规划(DP),它是一种用来解决一类最优化问题的算法思想。简单来说,动态规划将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。动态规划会将每个求解过的子问题的解记录下来,这样下一次碰到同样的子问题时,就可以直接用之前记录的结果,而不是重复计算。

下面介绍一下本题的解题思路:

  1. 令状态dp[i]表示以data[i]结尾的连续序列的最大乘积,此时仅有两种情况:
  • 这个最大乘积的连续序列只有一个元素,即以data[i]开始,以data[i]结束;
  • 这个最大乘积的连续序列有多个元素,即从前面某处data[p] (p<i)开始,一直到data[i]结尾。

对第一种情况,最大乘积就是data[i]本身;对第二种情况,最大c乘积是dp[i-1] * data[i],即data[p] * ... * data[i-1] * data[i] = dp[i-1] * data[i]。

由此两种情况,可得到状态转移方程:

dp[i] = max{data[i] ,dp[i-1] * data[i]}

这个式子只和i与i之前的元素有关,且边界为dp[0]=data[0],由小到大枚举i,即可得到整个dp数组,此数组中的最大值即为最大连续序列乘积。

  1. 而本题希望得到的是这个最大乘积子序列的区间。因此在计算dp[i]时根据上述两种情况记录该连续序列的起点。这里设置了一个start数组,来记录每个以data[i]结尾的最大乘积序列的起点,边界设置为1,即第一个元素。

在进行1.中情况判断时,若data[i] \leq dp[i - 1] * data[i],则将start[i]出起点设置为前一个起点start[i-1],否则以此data[i]为起点,即记录起点为i。最后遍历dp数组找最大值时,同时记录最大值所在位置对应的start起点。输出最终答案。

解题代码如下:

def solution(n, data):
    # Edit your code here
    start = [float('-inf')] * n
    dp = [float('-inf')] * n  # 注意要写无穷小
    # 边界
    dp[0] = data[0]
    start[0] = 1
    for i in range(1, n):
        #  状态转移方程
        dp[i] = max(data[i], dp[i - 1] * data[i])
        if data[i] <= dp[i - 1] * data[i]:
            start[i] = start[i - 1]
        else:
            start[i] = i + 1
    #  dp[i]中存放以A[i]结尾的连续序列的最大和,需要遍历i得到最大的才是结果
    max_value = float('-inf')
    st,end=0,0
    for i in range(len(dp)):
        if dp[i] > max_value:
            max_value = dp[i]
            end = i + 1
            st = start[i]
    re=[st,end]
    return re