跳格子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
题解
动态规划:
- 创建一个数组 dp,其中 dp[i] 表示跳到 score[i] 时能得到的最大得分。
- 状态转移方程:dp[i] = max(dp[i-1],dp[i-2],...,dp[i-k]) + score[i];
- 但只是这样只能过40%的测试用例
大顶堆优化:
- 使用大顶堆来维护前 k 个dp 值,以便在每一步更新 dp[i] 时直接从堆顶取得最大值。
- 这样大概能过95%的测试用例
单调队列优化:
- 使用双向队列从尾部添加dp[i]的下标i,添加之前判断队列尾部的下标last对应的元素dp[last]是否比dp[i]小
- dp[last]比dp[i]小,则将dp[last]取出丢弃。因为在dp[i]前面的比dp[i]还小的值不会被后面使用到,后面要的是最大值。
- 这样队列里保存的下标对应的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]);
}
}
查看7道真题和解析