Given an integer array nums
and an integer k
, return the maximum sum of a non-empty subsequence of that array such that for every two consecutive integers in the subsequence, nums[i]
and nums[j]
, where i < j
, the condition j - i <= k
is satisfied.
A subsequence of an array is obtained by deleting some number of elements (can be zero) from the array, leaving the remaining elements in their original order.
Example 1:
Input: nums = [10,2,-10,5,20], k = 2
Output: 37
Explanation: The subsequence is [10, 2, 5, 20].
Example 2:
Input: nums = [-1,-2,-3], k = 1
Output: -1
Explanation: The subsequence must be non-empty, so we choose the largest number.
Example 3:
Input: nums = [10,-2,-10,-5,20], k = 2
Output: 23
Explanation: The subsequence is [10, -2, -5, 20].
Constraints:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
题目链接:https://leetcode.com/problems/constrained-subsequence-sum/
题目大意:给一个数组,从中取数,要求相邻两数下标不超过k,求所能取得的数的和的最大值
题目分析:一开始想的做法是dp[i][0/1]表示位置i不取或取时的最大值,之后发现dp[i][1]不能通过dp[i][0]转移,因为dp[i][0]没法知道其之前某个位置是不是取了数字,于是直接将状态修改成dp[i]表示取第i个数字时的最大值,最后只要对dp数组求个max就是答案,现在的问题是相邻下标不超过k的限制如何实现,可以先写出转移方程:
dp[i] = max(nums[i], max(dp[i - 1], dp[i - 2], ..., dp[i - k]))
左边的nums[i]表示从i位置开始取,前面全不要
右边的max表示取i-1,i-2,一直到i-k的dp值,很明显的滑动窗,滑动窗维护区间最值可以参考这篇:https://blog.csdn.net/Tc_To_Top/article/details/82933983?spm=1001.2014.3001.5501
17ms,时间击败63.57%
class Solution {
public int constrainedSubsetSum(int[] nums, int k) {
int n = nums.length;
int[] dp = new int[n];
int ans = nums[0];
dp[0] = nums[0];
Deque<Integer> dq = new LinkedList<>();
dq.offerFirst(0);
//System.out.println("dp[0] = " + dp[0]);
for (int i = 1; i < n; i++) {
while (dq.size() != 0 && i - dq.peekFirst() > k) {
dq.pollFirst();
}
dp[i] = Math.max(nums[i], nums[i] + dp[dq.peekFirst()]);
//System.out.println("dp[" + i + "] = " + dp[i]);
ans = Math.max(ans, dp[i]);
while (dq.size() != 0 && dp[dq.peekLast()] < dp[i]) {
dq.pollLast();
}
dq.offerLast(i);
}
return ans;
}
}
改成用数组模拟双端队列后:
5ms,时间击败100%
class Solution {
public int constrainedSubsetSum(int[] nums, int k) {
int[] dp = new int[nums.length];
int ans = nums[0];
dp[0] = nums[0];
int[] dq = new int[nums.length + 1];
int l = 0, r = 0;
dq[++r] = 0;
for (int i = 1; i < nums.length; i++) {
while (l <= r && i - dq[l] > k) {
l++;
}
dp[i] = Math.max(nums[i], nums[i] + dp[dq[l]]);
ans = Math.max(ans, dp[i]);
while (l <= r && dp[dq[r]] < dp[i]) {
r--;
}
dq[++r] = i;
}
return ans;
}
}