//给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。
//
// 一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1, min(n - 1,
//i + k)] 包含 两个端点的任意位置。
//
// 你的目标是到达数组最后一个位置(下标为 n - 1 ),你的 得分 为经过的所有数字之和。
//
// 请你返回你能得到的 最大得分 。
//
//
//
// 示例 1:
//
//
//输入:nums = [1,-1,-2,4,-7,3], k = 2
//输出:7
//解释:你可以选择子序列 [1,-1,4,3] (上面加粗的数字),和为 7 。
//
//
// 示例 2:
//
//
//输入:nums = [10,-5,-2,4,0,3], k = 3
//输出:17
//解释:你可以选择子序列 [10,4,3] (上面加粗数字),和为 17 。
//
//
// 示例 3:
//
//
//输入:nums = [1,-5,-20,4,-1,3,-6,-3], k = 2
//输出:0
//
//
//
//
// 提示:
//
//
// 1 <= nums.length, k <= 105
// -104 <= nums[i] <= 104
//
// 👍 17 👎 0
这是220周周赛的第三题,在当时没做出来,脑子里想了多种方式,把自己绕进去了。
先看题,很显然是个dp,而且是很容易想出来的那种dp。
再看数据规模,估算一下,暴力dp的话,时间复杂度要到O(nk), 大概率是超时的。于是当时写出代码后也没有提交,在考虑优化。
首先想到的就是贪心,因为在i位置能够向前找到第一个正数的话,那么一定会经过这个正数。
但是实际很难编写,因为有可能出现前k步里一个正数都没有都情况。
再后想到的优化方法就是使用一个数组保存当前位置k步以内的最大分数位置,这个实际上很简单,用优先队列将分数和索引保存即可,但是比赛时并没有想到。
代码如下:
public int maxResult(int[] nums, int k) {
// 创建优先队列,用于保存位置i和对应i位置的分数
// 按照分数的大小排列。
PriorityQueue<int[]> queue = new PriorityQueue<>((o1, o2) -> { return o2[0] - o1[0]; });
// 0位置的分数就是nums[0]
queue.offer(new int[]{nums[0], 0});
int res = nums[0];
for (int i = 1; i < nums.length; i++) {
// 如果最大分数距离i位置都距离已经超过k,那么这个位置都分数就已经没有价值了。
// 将之出队列,使用后面的分数
while (i - queue.peek()[1] > k){
queue.poll();
}
// 当前位置的得分就是k步以内的最大分数加上当前位置的分数
res = queue.peek()[0] + nums[i];
// 将i位置的分数和索引放到优先队列里
queue.offer(new int[]{res, i});
}
// 目标就是最后一步的得分,刚好res到最后一步就截止了
return res;
}
比赛时都想到了解决方法,但是并没有想到实际的去使用,还是暴露了题目写少了,而且对优先队列这个数据结构使用不熟练的原因。