最大系列
- 1.LeetCode-239 滑动窗口的最大值
- 2.LeetCode-53 连续子数组的最大和
- 3.LeetCode-152 乘积最大的子数组。
- 4.剑指 Offer 14- I. 剪绳子为k个整数段,使各个段成绩最大
- 1.dp
- 数学推导
1.LeetCode-239 滑动窗口的最大值
窗口由左往右最大值数组Left,和由右向左最大值数组right维护。
在这里插入代码片
def maxSlidingWindow(self, nums: 'List[int]', k: 'int') -> 'List[int]':n = len(nums)if n * k == 0:return []if k == 1:return numsleft = [0] * nleft[0] = nums[0]right = [0] * nright[n - 1] = nums[n - 1]for i in range(1, n):# from left to rightif i % k == 0:# block startleft[i] = nums[i]else:left[i] = max(left[i - 1], nums[i])# from right to leftj = n - i - 1 # i = 1, j = n-2if (j + 1) % k == 0:# block endright[j] = nums[j]else:right[j] = max(right[j + 1], nums[j])output = []for i in range(n - k + 1): # i=[0,n-k)output.append(max(left[i + k - 1], right[i]))return output
2.LeetCode-53 连续子数组的最大和
借助连续性,如果某一块的和为负数,这一块不会作为和最大连续子数组的开头,因为去掉这个部分,后半段加和会更大。
动态规划的解题思路:dp[i] 以nums[i]结尾的连续子数组的最大和,dp[i] 只与dp[i-1]有关,空间约减后,空间复杂度为0(1)
def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""res = float("-INF")dp = 0for val in nums:dp += val # [-1] 先加,更新答案,确定是否归零res = max(dp, res)dp = max(dp, 0)return res
3.LeetCode-152 乘积最大的子数组。
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
直接思路:fmax(i)f_{max}(i)fmax(i)表示以第i个元素结尾的乘积最大的子数组的积,状态转移方程为
fmax(i)=maxi=1n{f(i−1)∗ai,ai}f_{max}(i)=\max_{i=1}^n\{f(i-1)*a_i,a_i\} fmax(i)=i=1maxn{f(i−1)∗ai,ai}
即,fmax(i)f_{max}(i)fmax(i)可以考虑nums[i]加入前面fmax(i−1)f_{max}(i-1)fmax(i−1)对应的一段,或者自成一段,这两种情况取最大值,求出所有的f_i之后,选取一个最大的作为结果。
核心问题:当前位置的最优解不一定是由前一个位置的最优解得到。因为存在正负数
如果当前的数是负数的话,我们希望以num[i-1]为结尾的某一个段的积也是一个负数,负负可以为正。
如果当前数为正,我们希望以num[i-1]为结尾的某一个段的积也是一个正数,正的越多,乘积完越大。
所以再维护一个fmin(i)f_{min}(i)fmin(i)表示以第i个元素结尾的乘积最小的子数组的积:
fmax(i)=max{fmax(i−1)∗ai,fmin(i−1)∗ai,ai}fmin(i)=min{fmax(i−1)∗ai,fmin(i−1)∗ai,ai}f_{max}(i)=\max\{f_{max}(i-1)*a_i,f_{min}(i-1)*a_i,a_i\}\\ f_{min}(i)=\min\{f_{max}(i-1)*a_i,f_{min}(i-1)*a_i,a_i\}fmax(i)=max{fmax(i−1)∗ai,fmin(i−1)∗ai,ai}fmin(i)=min{fmax(i−1)∗ai,fmin(i−1)∗ai,ai}
def maxProduct(self, nums):""":type nums: List[int]:rtype: int"""pre_max, pre_min = 1, 1res = float("-INF")for val in nums:cur_max = max(pre_max * val, pre_min * val, val)cur_min = min(pre_max * val, pre_min * val, val)pre_max, pre_min = cur_max, cur_minres = max(res, cur_max)return res
4.剑指 Offer 14- I. 剪绳子为k个整数段,使各个段成绩最大
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
1.dp
dp[i]: 长度为i 绳子至少剪了一次的最长长度
dp[i]=max(dp[j]∗(i−j),j∗(i−j),dp[i]),j∈[1,i−1]dp[i] = max(dp[j]*(i-j),j*(i-j),dp[i]),j\in[1,i-1]dp[i]=max(dp[j]∗(i−j),j∗(i−j),dp[i]),j∈[1,i−1]
class Solution(object):def cuttingRope(self, n):""":type n: int:rtype: int"""dp = [0]*(n+1)dp[1] = 1for i in range(2,n+1):for j in range(1,i):dp[i]= max(dp[i],dp[j]*(i-j),j*(i-j))return dp[-1]
n^2复杂度的DP
数学推导
通过数学不等式推到,可以得到,当每段长度为3时乘积最大。所以尽可能分为三段,最后一段依据具体情况判断:
class Solution(object):def cuttingRope(self, n):""":type n: int:rtype: int"""if n<=3:return n-1s, mod = n //3, n % 3 if mod == 0:res = 3**selif mod == 1:res = 3**(s-1)*4 else:res = 3**s*2return res%(10**9+7)