跳格子3 - 华为OD统一考试(C卷)-免费看题解

OD统一考试(C卷)

分值: 200分

题解: Java / Python / C++

题目描述

小明和朋友们一起玩跳格子游戏,

每个格子上有特定的分数 score = [1, -1, -6, 7, -17, 7],

从起点score[0]开始,每次最大的步长为k,请你返回小明跳到终点 score[n-1] 时,能得到的最大得分。

输入描述

第一行输入总的格子数量 n

第二行输入每个格子的分数 score[i]

第三行输入最大跳的步长 k

输出描述

输出最大得分

备注

  • 格子的总长度 n 和步长 k 的区间在 [1, 100000]
  • 每个格子的分数 score[i] 在 [-10000, 10000] 区间中

示例1

输入:
6
1 -1 -6 7 -17 7
2

输出:
14

说明:
输出最大得分数,小明从起点score[0]开始跳,第一次跳score[1],第二次跳到score[3],第三次跳到score[5],
因此得到的最大的得分是score[0] + score[1] + score[3] + score[5] = 14

题解

动态规划:

  1. 创建一个数组 dp,其中 dp[i] 表示跳到 score[i] 时能得到的最大得分。
  2. 状态转移方程:dp[i] = max(dp[i-1],dp[i-2],...,dp[i-k]) + score[i];
  3. 但只是这样只能过40%的测试用例

大顶堆优化:

  1. 使用大顶堆来维护前 k 个dp 值,以便在每一步更新 dp[i] 时直接从堆顶取得最大值。
  2. 这样大概能过95%的测试用例

单调队列优化:

  1. 使用双向队列从尾部添加dp[i]的下标i,添加之前判断队列尾部的下标last对应的元素dp[last]是否比dp[i]小
  2. dp[last]比dp[i]小,则将dp[last]取出丢弃。因为在dp[i]前面的比dp[i]还小的值不会被后面使用到,后面要的是最大值。
  3. 这样队列里保存的下标对应的dp元素是单调递减的。较小的元素直接淘汰,无需多次排序。

动态规划+大顶堆:

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	while (in.hasNextInt()) {
		long start = System.currentTimeMillis();
		int n = in.nextInt();
		int[] arr = new int[n];// 每个格子的分数,格子最少长度为1,格子分数可以为负数
		for (int i = 0; i < n; i++) {
			arr[i] = in.nextInt();
		}
		if (arr.length == 1){
			System.out.println(arr[0]);
			continue;
		}
		int k = in.nextInt();// 最大步长,即步长为1-k之间
		int[] dp = new int[arr.length];
		PriorityQueue<Integer> queue = new PriorityQueue<>(Collections.reverseOrder());
		for (int i = 0; i < arr.length; i++) {
			if (i==0){
				dp[i] = arr[0];
			}else {
				dp[i] = queue.peek() + arr[i];
			}
			queue.add(dp[i]);
			if (i-k>=0){
				queue.remove(dp[i-k]);
			}
		}
		System.out.println(dp[dp.length-1]);
	}
}

动态规划+单调队列:

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	while (in.hasNextInt()) {
		int n = in.nextInt();
		int[] arr = new int[n];// 每个格子的分数,格子最少长度为1,格子分数可以为负数
		for (int i = 0; i < n; i++) {
			arr[i] = in.nextInt();
		}
		if (arr.length == 1){
			System.out.println(arr[0]);
			continue;
		}
		int k = in.nextInt();// 最大步长,即步长为1-k之间
		int[] dp = new int[arr.length];
		// 使用一个双端队列来维护单调递减的索引
		Deque<Integer> deque = new LinkedList<>();
		for (int i = 0; i < arr.length; i++) {
			// 移除队列中超出步长限制的索引
			if (!deque.isEmpty() && i - deque.peekFirst() > k) {
				deque.pollFirst();
			}
			// 更新当前位置的最大得分
			dp[i] = (deque.isEmpty() ? 0 : dp[deque.peekFirst()]) + arr[i];
			// 保持单调递减性质,比当前dp[i]还小的dp[i-x]已经没有用了,要取也是取当前dp[i]或前面更大的值
			while (!deque.isEmpty() && dp[i] >= dp[deque.peekLast()]) {
				// 队列中无用的索引移除
				deque.pollLast();
			}
			// 将当前索引加入队列
			deque.offerLast(i);
			// 对于dp数组 8 5 4 3 7 0 0,假如步长k=4,arr[4]=-1
			// i=4时队列里存的dp的索引index为 0 1 2 3,其对应的dp元素是递减的
			// 计算dp[4] = 8 + arr[4] = 7
			// 此时,dp数组中dp[1] dp[2] dp[3]都比dp[4] 小,将队列中的对应index移除
			// 最后添加当前索引i=4到队列末尾,此时队列中的index对应的dp元素还是递减的
		}
		System.out.println(dp[dp.length-1]);
	}
}

全部评论

相关推荐

头像 头像
05-04 23:09
C++
点赞 评论 收藏
转发
3 3 评论
分享
牛客网
牛客企业服务