这道题如果暴力来做,即枚举左端点和右端点,需要O()的复杂度。,而计算区间内元素相乘又需要O(n)的复杂度,因此总复杂度为O()。即使采用记录前缀乘积的方法,使计算复杂度变为O(1),总复杂度仍然有O()。下面介绍动态规划的做法,仅需遍历一次数组,复杂度为O(n)。
首先介绍一下动态规划(DP),它是一种用来解决一类最优化问题的算法思想。简单来说,动态规划将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。动态规划会将每个求解过的子问题的解记录下来,这样下一次碰到同样的子问题时,就可以直接用之前记录的结果,而不是重复计算。
下面介绍一下本题的解题思路:
- 令状态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数组,此数组中的最大值即为最大连续序列乘积。
- 而本题希望得到的是这个最大乘积子序列的区间。因此在计算dp[i]时根据上述两种情况记录该连续序列的起点。这里设置了一个start数组,来记录每个以data[i]结尾的最大乘积序列的起点,边界设置为1,即第一个元素。
在进行1.中情况判断时,若data[i] 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