//给你一个下标从 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;}
比赛时都想到了解决方法,但是并没有想到实际的去使用,还是暴露了题目写少了,而且对优先队列这个数据结构使用不熟练的原因。
