题目描述
给你一个整数数组 nums 和一个整数 k ,请你返回「非空」子序列元素和的最大值,子序列需要满足:子序列中每两个「相邻」的整数 nums[i] 和 nums[j],它们在原数组中的下标 i 和 j 满足 i < j 且 j - i <= k 。
数组的子序列定义为:将数组中的若干个数字删除(可以删除 0 个数字),剩下的数字按照原本的顺序排布。
示例 1
输入:nums = [10,2,-10,5,20], k = 2
输出:37
解释:子序列为 [10, 2, 5, 20] 。
1
2
3
示例 2
输入:nums = [-1,-2,-3], k = 1
输出:-1
解释:子序列必须是非空的,所以我们选择最大的数字。
1
2
3
示例 3
输入:nums = [10,-2,-10,-5,20], k = 2
输出:23
解释:子序列为 [10, -2, -5, 20] 。
1
2
3
提示
1 <= k <= nums.length <= 1e5
-1e4 <= nums[i] <= 1e4
题目讲解
本题求解的是最大「非空」子序列元素和,且相邻两个元素坐标差小于等于 k。由于题目的主体是子序列,因此我们采取一种「增量式」的思想来进一步思考。
假设当前有一个子序列 A,现在想在 A 后面再添加一个元素 x,则我们只需要考虑 x 和 A 中最后一个元素的坐标差是否小于等于 k,而不用考虑 A 中的所有元素。更明确地说,对于子序列 A,我们只需要记录它的元素和与最后一个元素的下标即可。
基于上述的思考,不难想到使用动态规划的算法,令 f[i] 表示以 nums[i] 为子序列最后一个元素时的最大元素和,则可以得到如下递推公式:
f [ i ] = max ( f [ j ] , 0 ) + n u m s [ i ] ( i − k ≤ j < i ) f[i]=\max(f[j],0)+nums[i]\ \ \ (i-k\leq j<i)
f[i]=max(f[j],0)+nums[i] (i−k≤j<i)
根据上述公式,我们可以得到一种暴力的做法,即对于每一个 i,向前枚举合法的 j 来更新 f[i],时间复杂度为 O(nk)。
观察题目中的数据范围,暴力做法很明显无法通过,因此我们考虑如何优化。
f[i] 由前面 k 个数中的最大值转移而来,因此不难想到使用「单调队列」算法来进行优化。用「单调队列」来维护 f 数组中大小为 k 的窗口的最大值即可完成此题,时间复杂度优化至 O(n),具体细节见代码。
通过此题,我们可以更深刻地意识到,「单调队列」在求取「数组中每一个元素其固定区间范围内的最大 / 小值」的作用。而也正是该功能,使得「单调队列」常作为「动态规划」的一种优化手段出现在面试题中。
————————————————
版权声明:本文为CSDN博主「Gene_INNOCENT」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41552508/article/details/114909195