【2024年华为OD机试】(B卷,100分)- 找终点 (Java JS PythonC/C++)

在这里插入图片描述

一、问题描述

题目描述

给定一个正整数数组,设为 nums,最大为100个成员,求从第一个成员开始,正好走到数组最后一个成员,所使用的最少步骤数。

要求:

  • 第一步必须从第一元素开始,且 1<=第一步的步长<len/2;(len为数组的长度,需要自行解析)。
  • 从第二步开始,只能以所在成员的数字走相应的步数,不能多也不能少。如果目标不可达返回-1,只输出最少的步骤数量。
  • 只能向数组的尾部走,不能往回走。

输入描述

由正整数组成的数组,以空格分隔,数组长度小于100,请自行解析数据数量。

输出描述

正整数,表示最少的步数,如果不存在输出-1。

用例

输入

7 5 9 4 2 6 8 3 5 4 3 9

输出

2

说明
第一步:第一个可选步长选择2,从第一个成员7开始走2步,到达9;

第二步:从9开始,经过自身数字9对应的9个成员到最后。

输入

1 2 3 7 1 5 9 3 2 1

输出

-1

说明

题目解析

首先,这题至少要走两步,因为第一步的步长是自选的,只要小于 len/2,即可,也就是说第一步走完,应该处于 [1, len/2] 的范围中,我们假设处于 A 位置。

而第二步开始,每次走的步长都是前一步结束所在位置的值,比如第二步要走的步长就是 A 位置的值,因此有三种可能:

  1. 第二步要走的步长刚好到达 arr.length-1 位置

    • 则最短步数就是2。
  2. 第二步要走的步长小于 arr.length-1 位置

    • 则说明需要继续往下走,逻辑还是第二步的逻辑,递归下去。每次走到新位置后,检查该位置的值是否能让我们到达或超过数组末尾。如果可以,记录步数并结束;如果不能,继续前进。
  3. 第二步要走的步长大于 arr.length-1 位置

    • 则说明当前路径走不通,需要回溯到上一步,尝试其他可能的步长(如果第一步有多种选择的话)。如果所有可能的步长都无法使我们到达末尾,则最终返回-1。
解题思路
  1. 第一步的选择:从第一个元素开始,选择一个步长,范围在 [1, len/2) 之间。这一步决定了我们接下来的位置。
  2. 后续步骤的递归:从新位置开始,根据当前位置的值决定下一步的步长。递归地进行这一步,直到:
    • 到达或超过数组末尾,记录步数。
    • 发现无法前进(即下一步会超出数组范围),回溯或标记为不可达。
  3. 结果判定:如果在尝试所有可能的第一步后,都无法到达末尾,则返回-1。否则,返回记录的最小步数。

这种方法确保我们考虑了所有可能的路径,从而找到到达末尾的最少步数。时间复杂度主要取决于数组长度和第一步的选择数量,最坏情况下为O(n^2),其中n是数组的长度。

二、JavaScript算法源码

以下是对您提供的 JavaScript 代码的中文详细注释和讲解。该代码通过 ACM 模式 从控制台获取输入,实现了一个算法来计算在给定数组中到达数组末尾所需的最小步数。每一步中,可以从一个位置跳到该位置上的值所指示的位置。

完整代码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");  // 引入 readline 模块,用于处理控制台输入const rl = readline.createInterface({input: process.stdin,  // 从标准输入读取数据output: process.stdout, // 输出到标准输出
});// 当接收到一行输入时进行处理
rl.on("line", (line) => {const arr = line.split(" ").map((ele) => parseInt(ele)); // 将输入的字符串用空格分割,并转换为整数数组console.log(getMinStep(arr)); // 调用算法函数计算最小步数并输出结果
});/* 算法函数 */
function getMinStep(arr) {let res = [];  // 用于存储所有可能的步数for (let i = 1; i < Math.floor(arr.length / 2); i++) {  // 从数组的第二个元素开始循环到数组的中部// 将每一种可能的路径所需要的步数存入 res 数组中res.push(loop(arr, i, 2));  // 调用递归函数计算步数,初始步数设为 2(因为第一次跳转已经算了一次)}// 过滤掉负值(表示无法到达末尾的路径),并按从小到大排序let step = res.filter((ele) => ele > 0).sort((a, b) => a - b)[0];  // 取最小的正数,即最小步数return step ? step : -1;  // 如果没有有效步数,则返回 -1
}/* 递归函数:计算从索引 i 开始,到达数组末尾所需的步数 */
function loop(arr, i, count) {let j = i + arr[i];  // 计算跳转后的新位置if (j === arr.length - 1) {  // 如果跳转到了数组的最后一个位置return count;  // 返回步数} else if (j < arr.length - 1) {  // 如果跳转位置在数组的有效范围内count++;  // 步数加 1return loop(arr, j, count);  // 继续递归跳转} else {return -1;  // 跳转位置超出数组范围,返回 -1 表示此路径不可行}
}

详细注释与讲解

1. 控制台输入处理
const readline = require("readline");const rl = readline.createInterface({input: process.stdin,output: process.stdout,
});
  • readline 模块:用于从控制台读取输入数据。
  • rl.on("line", (line) => {...}):当用户在控制台输入一行并按下回车时,触发该事件,line 是输入的一行字符串。
2. 输入处理逻辑
rl.on("line", (line) => {const arr = line.split(" ").map((ele) => parseInt(ele));  // 将输入的字符串用空格分割,并转换为整数数组console.log(getMinStep(arr)); // 调用算法函数计算最小步数并输出结果
});
  • 假设输入为 "2 3 1 1 4",通过 split(" ") 得到数组 ["2", "3", "1", "1", "4"]
  • 使用 .map((ele) => parseInt(ele)),将字符串数组转换为整数数组:[2, 3, 1, 1, 4]
3. getMinStep 算法解析
function getMinStep(arr) {let res = [];  // 用于存储所有可能的步数for (let i = 1; i < Math.floor(arr.length / 2); i++) {  // 从数组的第二个元素开始循环到数组的中部res.push(loop(arr, i, 2));  // 调用递归函数计算步数}let step = res.filter((ele) => ele > 0).sort((a, b) => a - b)[0];  // 取最小的正数,即最小步数return step ? step : -1;  // 如果没有有效步数,则返回 -1
}
  • 目标:从数组中找到一条路径,使得从数组的第一个元素开始跳到最后一个元素,所需的步数最少。
  • 逻辑
    1. res 数组用于存储所有不同的跳跃路径所需要的步数。
    2. for 循环从数组的第二个元素开始尝试跳转。因为如果从第一个元素直接跳,通常就被认为是第一步,所以这里 i1 开始。
    3. loop 函数是递归函数,负责以给定的起始点 i 进行跳转,并返回所需的步数。
    4. res 数组中过滤并排序所有可能的步数,取最小的正数作为结果,如果没有有效步数返回 -1
4. loop 递归函数解析
function loop(arr, i, count) {let j = i + arr[i];  // 计算跳转后的新位置if (j === arr.length - 1) {  // 如果跳转到了数组的最后一个位置return count;  // 返回当前步数} else if (j < arr.length - 1) {  // 如果跳转位置在数组的有效范围内count++;  // 步数加 1return loop(arr, j, count);  // 继续递归跳转} else {return -1;  // 跳转位置超出数组范围,返回 -1 表示此路径不可行}
}
  • 目标:递归模拟每次跳转并计算所需的步数。
  • 逻辑
    1. let j = i + arr[i]:根据当前位置 i 和数组中的值,计算新的跳转位置 j
    2. 终止条件
      • 如果跳转到了数组的最后一个位置,则返回当前步数 count
      • 如果跳转超出了数组的范围,返回 -1 表示此路径不可行。
    3. 递归跳转:如果跳转位置合法且没有到达最后一个位置,继续递归跳转,步数 count 加 1。
5. 时间复杂度分析
  • 递归调用 loop 的复杂度

    • 在最坏的情况下,loop 可能递归调用 n 次(n 是数组的长度)。
    • 因此,每个递归路径的最大时间复杂度是 O(n)
  • 整体时间复杂度

    • 外层 for 循环最多执行 Math.floor(arr.length / 2) 次,每次调用递归函数 loop
    • 整体时间复杂度近似为 O(n^2)(因为递归函数会反复调用,取决于具体路径的数量)。
示例解释

输入:

2 3 1 1 4
  • 该数组表示:
    • arr[0] = 2:可以从位置 0 跳到位置 2。
    • arr[1] = 3:可以从位置 1 跳到位置 4。
    • 其他同理。

执行步骤:

  • i = 1 开始跳转,调用 loop
    • 跳转路径:1 -> 3 -> 4,步数为 2。
  • 结果:
    • 最小的步数是 2,故输出 2。

总结

  1. 算法核心:使用递归模拟从数组不同位置的跳转过程,并找出到达数组末尾的最小步数。
  2. 递归作用:递归函数 loop 实现了一种回溯方法,反复尝试从某个位置跳到下一个合法位置,直至跳到末尾或超出范围。
  3. 优化空间:可通过动态规划或贪心算法进一步优化,避免多次重复递归带来的开销。

这段代码基于 ACM 模式 实现了通过控制台获取输入并计算结果,使用递归算法来解决数组跳转问题。希望这段中文注释与讲解能帮助您更好地理解代码逻辑和工作原理。

三、Java算法源码

以下是对您提供的 Java 代码的中文详细注释和讲解。该代码旨在通过控制台输入一个整数数组,然后计算从数组的开头跳到末尾所需的最小步数。每一步可以根据当前位置的值跳到新的位置。

代码全文

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;public class Main {// 主函数:程序的入口public static void main(String[] args) {Scanner sc = new Scanner(System.in);  // 创建一个 Scanner 对象,用于从控制台获取输入// 通过输入一行字符串,按照空格分割,并将其转换为 Integer 数组Integer[] arr = Arrays.stream(sc.nextLine().split(" ")).map(Integer::parseInt).toArray(Integer[]::new);// 调用 getResult 函数,传入数组,并打印结果System.out.println(getResult(arr));}// 主逻辑函数:计算从数组开头跳到末尾的最小步数public static int getResult(Integer[] arr) {ArrayList<Integer> res = new ArrayList<>();  // 创建一个 ArrayList,用于存储所有可能的跳跃步数// 从数组的第二个元素开始,尝试以每个位置作为起始跳转点,进行递归计算步数for (int i = 1; i < arr.length / 2; i++) {res.add(loop(arr, i, 2));  // 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2}// 过滤掉负值(负值表示无法到达末尾),然后找出最小的正值步数return res.stream().filter(ele -> ele > 0).min((a, b) -> a - b).orElse(-1);}// 递归函数:从数组 arr 的索引 i 开始跳转,返回跳到数组末尾所需的步数public static int loop(Integer[] arr, int i, int count) {int j = i + arr[i];  // 计算跳转后的新位置if (j == arr.length - 1) {  // 如果跳转到了数组的最后一个位置return count;  // 返回当前的步数,表示成功跳到末尾} else if (j < arr.length - 1) {  // 如果跳转位置在数组有效范围内count++;  // 步数增加 1return loop(arr, j, count);  // 递归调用,从新的位置继续跳转} else {  // 如果跳转超出了数组范围return -1;  // 返回 -1,表示此路径不可行}}
}

详细注释和讲解

1. 主函数(main 方法)
public static void main(String[] args) {Scanner sc = new Scanner(System.in);Integer[] arr = Arrays.stream(sc.nextLine().split(" ")).map(Integer::parseInt).toArray(Integer[]::new);System.out.println(getResult(arr));
}
  • 作用
    • 这是程序的入口点,负责处理控制台输入并调用核心算法函数。
  • 详细解析
    • Scanner sc = new Scanner(System.in);:创建一个 Scanner 对象,用于从控制台读取用户输入。
    • sc.nextLine():读取一整行输入(例如:"2 3 1 1 4")。
    • .split(" "):将输入字符串按照空格分割成字符串数组(例如:["2", "3", "1", "1", "4"])。
    • Arrays.stream(...).map(Integer::parseInt):将字符串数组中的每个元素转换为整数。
    • .toArray(Integer[]::new):将流转换回整数数组 Integer[] arr(例如:[2, 3, 1, 1, 4])。
    • getResult(arr):调用核心算法函数 getResult 计算最小步数。
    • System.out.println(...):打印结果。
2. 主逻辑函数(getResult 方法)
public static int getResult(Integer[] arr) {ArrayList<Integer> res = new ArrayList<>();  // 用于存储所有跳跃路径的步数for (int i = 1; i < arr.length / 2; i++) {res.add(loop(arr, i, 2));  // 从每个位置 i 开始进行递归跳转,初始步数为 2}// 过滤掉负值(表示无法成功跳到末尾的路径),并找到最小步数return res.stream().filter(ele -> ele > 0).min((a, b) -> a - b).orElse(-1);
}
  • 作用
    • 该函数计算从数组的开头跳到末尾所需的最小步数。
  • 详细解析
    • ArrayList<Integer> res:用来存储从不同位置开始跳跃所需的总步数。
    • for 循环从数组的第二个元素开始尝试(即索引 1),调用递归函数 loop 计算从位置 i 跳到末尾的步数。
    • res.add(loop(arr, i, 2)):将每次跳跃的步数结果加入 res 数组中,初始步数从 2 开始,因为第一次跳转通常已经算做一步。
    • 使用 Java 8 的 Stream API 来过滤掉无效路径(步数为负值),并对剩余的步数进行排序,找出最小的步数。
    • .orElse(-1):如果没有有效步数(即所有路径都无法到达末尾),则返回 -1
3. 递归函数(loop 方法)
public static int loop(Integer[] arr, int i, int count) {int j = i + arr[i];  // 计算跳转后的新位置if (j == arr.length - 1) {  // 如果跳到了数组的最后一个位置return count;  // 返回当前步数,表示成功跳到末尾} else if (j < arr.length - 1) {  // 如果跳转后位置在数组范围内count++;  // 步数加 1return loop(arr, j, count);  // 继续递归跳转到下一个新位置} else {  // 如果跳转后位置超出数组范围return -1;  // 返回 -1,表示此路径不可行}
}
  • 作用
    • 递归函数 loop 用于模拟从数组的某个位置开始,反复跳转直到到达末尾或超出数组范围。
  • 详细解析
    • int j = i + arr[i];:计算跳转后的新位置 j。例如,当前位置 i1arr[i]3,则跳到的新位置为 1 + 3 = 4
    • if (j == arr.length - 1):如果跳到了数组的最后一个位置,则返回当前步数 count,表示成功跳到末尾。
    • else if (j < arr.length - 1):如果跳转后的新位置仍然在数组范围内,则步数 count 加 1,并继续递归调用 loop 函数跳转到新位置。
    • else:如果跳转超出数组范围,返回 -1 表示此路径不可行。

示例说明

输入
2 3 1 1 4
过程解析
  1. 从索引 0 开始,跳转 arr[0] = 2,跳到索引 2
  2. 从索引 2 开始,跳转 arr[2] = 1,跳到索引 3
  3. 从索引 3 开始,跳转 arr[3] = 1,跳到索引 4,成功跳到末尾。

通过多次递归调用 loop,可以找出从不同位置开始跳跃所需的最小步数。

输出
2

该输出表示从数组的开头跳到末尾的最小步数是 2


总结

  • 核心算法:代码使用递归方式模拟跳跃过程,通过 loop 函数递归跳转,并使用 getResult 函数遍历所有可能的起始跳转点,从而找出最小的跳跃步数。
  • 优化空间
    • 递归算法可能存在大量重复计算,尤其是当数组非常大时,可以考虑通过动态规划或贪心算法优化性能。
    • 但递归方式简单直观,适合学习理解跳跃问题的核心思想。

希望通过这段中文注释与讲解能帮助您更好地理解代码的工作原理和算法的实现过程。如果您有任何问题,欢迎进一步探讨!

四、Python算法源码

以下是对您提供的 Python 代码的中文详细注释和讲解。该代码通过控制台输入一个整数数组,然后计算从数组的开头跳到末尾所需的最小步数。每一步可以根据当前位置的值跳到新的位置。

代码全文

# 输入获取
arr = list(map(int, input().split()))# 递归函数:从数组 arr 的索引 i 开始跳转,返回跳到数组末尾所需的步数
def loop(arr, i, count):j = i + arr[i]  # 计算跳转后的新位置if j == len(arr) - 1:  # 如果跳转到了数组的最后一个位置return count  # 返回当前步数elif j < len(arr) - 1:  # 如果跳转后的新位置在数组有效范围内count += 1  # 步数加 1return loop(arr, j, count)  # 递归跳转到新的位置else:  # 如果跳转位置超出了数组范围return -1  # 返回 -1,表示此路径不可行# 算法入口函数:计算从数组开头跳到末尾的最小步数
def getResult():res = []  # 用于存储所有可能的跳跃步数for i in range(1, len(arr) // 2):  # 从数组的第二个元素开始尝试跳转res.append(loop(arr, i, 2))  # 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2# 过滤掉负值(负值表示无法成功跳到末尾的路径),并排序剩下的正值步数tmp = list(filter(lambda x: x > 0, res))tmp.sort()  # 对步数进行排序# 返回最小的正值步数,如果没有正值步数则返回 -1if len(tmp) > 0:return tmp[0]else:return -1# 算法调用:打印结果
print(getResult())

详细注释和讲解

1. 输入获取
arr = list(map(int, input().split()))
  • 作用

    • 从控制台获取输入的一行,并将其转换为整数列表。
  • 示例

    • 输入:"2 3 1 1 4"
    • 处理后:[2, 3, 1, 1, 4]
2. 递归函数 loop
def loop(arr, i, count):j = i + arr[i]  # 计算跳转后的新位置if j == len(arr) - 1:  # 如果跳转到了数组的最后一个位置return count  # 返回当前步数elif j < len(arr) - 1:  # 如果跳转后的新位置在数组有效范围内count += 1  # 步数加 1return loop(arr, j, count)  # 递归跳转到新的位置else:  # 如果跳转位置超出了数组范围return -1  # 返回 -1,表示此路径不可行
  • 作用

    • 模拟从数组某个位置开始跳转,直到跳到数组末尾或超出范围。
  • 详细解析

    • j = i + arr[i]:计算从当前位置 i 跳转后的新位置 j
    • if j == len(arr) - 1:如果跳到数组的最后一个位置,返回当前步数 count
    • elif j < len(arr) - 1:如果跳转后的新位置在数组有效范围内,步数 count 加 1,并递归调用 loop 函数跳转到新位置。
    • else:如果跳转位置超出数组范围,返回 -1 表示此路径不可行。
3. 算法入口函数 getResult
def getResult():res = []  # 用于存储所有可能的跳跃步数for i in range(1, len(arr) // 2):  # 从数组的第二个元素开始尝试跳转res.append(loop(arr, i, 2))  # 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2# 过滤掉负值(负值表示无法成功跳到末尾的路径),并排序剩下的正值步数tmp = list(filter(lambda x: x > 0, res))tmp.sort()  # 对步数进行排序# 返回最小的正值步数,如果没有正值步数则返回 -1if len(tmp) > 0:return tmp[0]else:return -1
  • 作用

    • 计算从数组开头跳到末尾的最小步数。
  • 详细解析

    • res = []:用于存储所有可能的跳跃步数。
    • for i in range(1, len(arr) // 2):从数组的第二个元素开始尝试跳转(i1len(arr) // 2)。
    • res.append(loop(arr, i, 2)):调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2
    • tmp = list(filter(lambda x: x > 0, res)):过滤掉负值(表示无法成功跳到末尾的路径)。
    • tmp.sort():对剩下的正值步数进行排序。
    • if len(tmp) > 0: return tmp[0]:返回最小的正值步数,如果没有正值步数则返回 -1
4. 算法调用
print(getResult())
  • 作用
    • 调用 getResult 函数,并打印结果。

示例说明

输入
2 3 1 1 4
过程解析
  1. 从索引 0 开始,跳转 arr[0] = 2,跳到索引 2
  2. 从索引 2 开始,跳转 arr[2] = 1,跳到索引 3
  3. 从索引 3 开始,跳转 arr[3] = 1,跳到索引 4,成功跳到末尾。
输出
2

该输出表示从数组的开头跳到末尾的最小步数是 2


总结

  • 核心算法:代码使用递归方式模拟跳跃过程,通过 loop 函数递归跳转,并使用 getResult 函数遍历所有可能的起始跳转点,从而找出最小的跳跃步数。
  • 优化空间
    • 递归算法可能存在大量重复计算,尤其是当数组非常大时,可以考虑通过动态规划或贪心算法优化性能。
    • 但递归方式简单直观,适合学习理解跳跃问题的核心思想。

希望通过这段中文注释与讲解能帮助您更好地理解代码的工作原理和算法的实现过程。如果您有任何问题,欢迎进一步探讨!

五、C/C++算法源码:

以下是将您提供的 Java 代码分别转换为 C++C 语言 代码,并附上详细的中文注释和讲解。两段代码的功能与原 Java 代码一致,即通过控制台输入一个整数数组,然后计算从数组的开头跳到末尾所需的最小步数。


C++ 代码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>using namespace std;// 递归函数:从数组 arr 的索引 i 开始跳转,返回跳到数组末尾所需的步数
int loop(vector<int>& arr, int i, int count) {int j = i + arr[i];  // 计算跳转后的新位置if (j == arr.size() - 1) {  // 如果跳到了数组的最后一个位置return count;  // 返回当前步数} else if (j < arr.size() - 1) {  // 如果跳转后的新位置在数组有效范围内count++;  // 步数加 1return loop(arr, j, count);  // 递归跳转到新的位置} else {  // 如果跳转位置超出了数组范围return -1;  // 返回 -1,表示此路径不可行}
}// 算法入口函数:计算从数组开头跳到末尾的最小步数
int getResult(vector<int>& arr) {vector<int> res;  // 用于存储所有可能的跳跃步数// 从数组的第二个元素开始尝试跳转for (size_t i = 1; i < arr.size() / 2; i++) {res.push_back(loop(arr, i, 2));  // 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2}// 使用 C++ 标准库的 min_element 函数找到最小的正值res.erase(remove_if(res.begin(), res.end(), [](int ele) { return ele <= 0; }), res.end());if (!res.empty()) {return *min_element(res.begin(), res.end());  // 返回最小步数} else {return -1;  // 如果没有有效步数,返回 -1}
}// 主函数:程序的入口
int main() {string input;getline(cin, input);  // 从控制台读取一整行输入// 使用 stringstream 将输入字符串按照空格分割并转换为整数,存入 vector 中vector<int> arr;stringstream ss(input);int num;while (ss >> num) {arr.push_back(num);}// 调用 getResult 函数计算最小步数,并输出结果cout << getResult(arr) << endl;return 0;
}

C++ 代码详解

1. loop 递归函数
int loop(vector<int>& arr, int i, int count) {int j = i + arr[i];  // 计算跳转后的新位置if (j == arr.size() - 1) {  // 如果跳到了数组的最后一个位置return count;  // 返回当前步数} else if (j < arr.size() - 1) {  // 如果跳转后的新位置在数组有效范围内count++;  // 步数加 1return loop(arr, j, count);  // 递归跳转到新的位置} else {  // 如果跳转位置超出了数组范围return -1;  // 返回 -1,表示此路径不可行}
}
  • 功能:模拟数组中的跳转过程,从给定的索引 i 开始跳转,直到跳到数组的末尾或者跳出数组范围。返回跳到末尾所需的最小步数。
  • 参数
    • arr:整数数组。
    • i:当前所在数组的索引位置。
    • count:当前已跳跃的步数。
2. getResult 主逻辑函数
int getResult(vector<int>& arr) {vector<int> res;  // 用于存储所有可能的跳跃步数// 从数组的第二个元素开始尝试跳转for (size_t i = 1; i < arr.size() / 2; i++) {res.push_back(loop(arr, i, 2));  // 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2}// 过滤掉负值,只保留正值(表示成功跳到末尾的路径)res.erase(remove_if(res.begin(), res.end(), [](int ele) { return ele <= 0; }), res.end());if (!res.empty()) {return *min_element(res.begin(), res.end());  // 找到最小的正值步数} else {return -1;  // 如果没有有效步数,返回 -1}
}
  • 功能:计算从数组的不同位置开始跳转到末尾的最小步数。遍历数组的中间位置,调用递归函数 loop 计算步数,并找到最小的正值步数。
3. main 主函数
int main() {string input;getline(cin, input);  // 读取整行输入// 使用 stringstream 将输入字符串按照空格分割并转换为整数,存入 vector 中vector<int> arr;stringstream ss(input);int num;while (ss >> num) {arr.push_back(num);}// 调用 getResult 计算最小步数并输出结果cout << getResult(arr) << endl;return 0;
}
  • 功能:从控制台读取一行输入,解析成整数数组,并调用 getResult 计算最小步数,最后输出结果。

C 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX 100// 递归函数:从数组 arr 的索引 i 开始跳转,返回跳到数组末尾所需的步数
int loop(int arr[], int i, int count, int len) {int j = i + arr[i];  // 计算跳转后的新位置if (j == len - 1) {  // 如果跳到了数组的最后一个位置return count;  // 返回当前步数} else if (j < len - 1) {  // 如果跳转后的新位置在数组有效范围内count++;  // 步数加 1return loop(arr, j, count, len);  // 递归跳转到新的位置} else {  // 如果跳转位置超出了数组范围return -1;  // 返回 -1,表示此路径不可行}
}// 辅助函数:比较两个整数大小,用于 qsort
int compare(const void* a, const void* b) {return (*(int*)a - *(int*)b);
}// 算法入口函数:计算从数组开头跳到末尾的最小步数
int getResult(int arr[], int len) {int res[MAX] = {0};  // 用于存储所有可能的跳跃步数int size = 0;  // 实际的步数数量// 从数组的第二个元素开始尝试跳转for (int i = 1; i < len / 2; i++) {res[size++] = loop(arr, i, 2, len);  // 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2}// 过滤掉负值,只保留正值int tmp[MAX];int tmpSize = 0;for (int i = 0; i < size; i++) {if (res[i] > 0) {tmp[tmpSize++] = res[i];}}// 如果有正值步数,则排序并返回最小步数if (tmpSize > 0) {qsort(tmp, tmpSize, sizeof(int), compare);return tmp[0];} else {return -1;  // 如果没有有效步数,返回 -1}
}int main() {char input[MAX];fgets(input, sizeof(input), stdin);  // 从控制台读取一整行输入// 解析输入的字符串,按照空格分割并转换为整数,存入数组中int arr[MAX];int num, len = 0;char* token = strtok(input, " ");while (token != NULL) {arr[len++] = atoi(token);token = strtok(NULL, " ");}// 调用 getResult 计算最小步数,并输出结果printf("%d\n", getResult(arr, len));return 0;
}

C 代码详解

1. loop 递归函数
int loop(int arr[], int i, int count, int len) {int j = i + arr[i];  // 计算跳转后的新位置if (j == len - 1) {  // 如果跳到了数组的最后一个位置return count;  // 返回当前步数} else if (j < len - 1) {  // 如果跳转后的新位置在数组有效范围内count++;  // 步数加 1return loop(arr, j, count, len);  // 递归跳转到新的位置} else {  // 如果跳转位置超出了数组范围return -1;  // 返回 -1,表示此路径不可行}
}
  • 功能:与 C++ 的 loop 函数相同,用于模拟跳转过程并计算步数。
2. getResult 主逻辑函数
int getResult(int arr[], int len) {int res[MAX] = {0};  // 用于存储所有可能的跳跃步数int size = 0;  // 实际的步数数量// 从数组的第二个元素开始尝试跳转for (int i = 1; i < len / 2; i++) {res[size++] = loop(arr, i, 2, len);  // 调用递归函数 loop,计算从位置 i 开始跳到末尾的步数,初始步数为 2}// 过滤掉负值,只保留正值int tmp[MAX];int tmpSize = 0;for (int i = 0; i < size; i++) {if (res[i] > 0) {tmp[tmpSize++] = res[i];}}// 如果有正值步数,则排序并返回最小步数if (tmpSize > 0) {qsort(tmp, tmpSize, sizeof(int), compare);returnif (tmpSize > 0) {qsort(tmp, tmpSize, sizeof(int), compare);return tmp[0];  // 返回排序后的最小步数} else {return -1;  // 如果没有有效步数,返回 -1}
}
  • 功能

    • loop 函数计算出多个跳跃步数后,对这些步数进行过滤和排序,找到最小的正值步数。
    • 如果存在有效的步数(即可以跳到数组末尾),则通过 qsort 对步数进行排序,并返回最小的步数。
    • 如果不存在有效的步数,则返回 -1
  • 详细解析

    • qsort(tmp, tmpSize, sizeof(int), compare):使用 C 标准库的 qsort 函数对步数数组 tmp 进行升序排序,排序规则由 compare 函数定义。
    • compare 函数很简单,只是比较两个整数的大小,返回正数、负数或零,具体实现为:
      int compare(const void* a, const void* b) {return (*(int*)a - *(int*)b);
      }
      
    • 最后,返回 tmp 数组中的第一个元素(即最小步数)。
3. 辅助函数 compare
int compare(const void* a, const void* b) {return (*(int*)a - *(int*)b);
}
  • 功能:该函数是一个标准的比较函数,用于 qsort 函数进行整数排序。它接收两个 void* 类型的参数,将它们转换为 int*,并返回它们的差值。这个差值用来决定两个数在排序中的顺序。
4. main 主函数
int main() {char input[MAX];fgets(input, sizeof(input), stdin);  // 从控制台读取一整行输入// 解析输入的字符串,按照空格分割并转换为整数,存入数组中int arr[MAX];int num, len = 0;char* token = strtok(input, " ");while (token != NULL) {arr[len++] = atoi(token);token = strtok(NULL, " ");}// 调用 getResult 计算最小步数,并输出结果printf("%d\n", getResult(arr, len));return 0;
}
  • 功能:主函数负责从控制台读取输入,解析成整数数组,并调用核心算法函数 getResult 来计算最小跳跃步数,最后输出结果。

  • 详细解析

    • fgets(input, sizeof(input), stdin):从标准输入读取一整行输入,并存储在 input 字符数组中。
    • strtok(input, " "):使用 strtok 函数按空格分割输入的字符串,并逐个转换为整数存入 arr 数组中。
    • atoi(token):将分割出来的字符串转换为整数。
    • getResult(arr, len):调用 getResult 函数计算最小步数并输出结果。

完整示例说明

输入
2 3 1 1 4
过程解析
  1. 输入解析

    • 通过 fgets 读取输入 "2 3 1 1 4"
    • 使用 strtok 分割后得到整数数组 arr = {2, 3, 1, 1, 4},长度 len = 5
  2. 计算最小步数

    • 从索引 1 开始跳转,递归函数 loop 依次计算步数:

      • 从索引 1 跳转:arr[1] = 3,跳到索引 1 + 3 = 4,成功跳到末尾,步数为 2
      • 从索引 2 开始跳转,递归过程也是跳到末尾,步数为 3
    • 所有的有效步数存储在 res 中,最终通过对 res 排序得到最小步数 2

输出
2

该输出表示从数组的开头跳到末尾的最小步数是 2


总结

C++ 和 C 代码的对比
  1. 代码结构

    • 两段代码的逻辑基本一致,核心算法和递归过程完全相同。
    • C++ 使用了 vector 动态数组,而 C 使用了定长数组 int arr[MAX],并且在处理输入时,C++ 使用了 stringstream,C 使用了 strtok
  2. C++ 特性

    • C++ 提供了更方便的标准库函数,例如 vectormin_elementremove_if 等,让代码更加简洁。
    • C++ 的 vector 可以自动管理数组大小,而 C 的定长数组需要定义一个较大的固定值 MAX
  3. C 特性

    • C 更加底层,需要手动编写更多代码,例如使用 strtok 进行字符串分割,并且通过 qsort 手动排序。
    • C 需要手动管理数组大小和内存分配。
优化建议
  • 虽然递归方式简单直观,但当数组非常大时,递归会导致大量的重复计算,性能较差。可以考虑使用动态规划或贪心算法来优化。

  • 动态规划思路:可以通过记录每个位置的最小步数,避免重复计算,这样时间复杂度会显著降低。

注意事项
  • 在 C 代码中,为了简单起见,数组大小被定义为了一个较大的常量 MAX。在实际使用中,应根据问题的规模适当调整 MAX 的值,或考虑动态内存分配(如使用 malloc)。

希望这段中文注释和讲解能够帮助您更好地理解 C++ 和 C 代码的实现。如有更多问题,欢迎继续讨论!

六、尾言

什么是华为OD?

华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。

为什么刷题很重要?

  1. 机试是进入技术面的第一关:
    华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。

  2. 技术面试需要手撕代码:
    技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。

  3. 入职后的可信考试:
    入职华为后,还需要通过“可信考试”。可信考试分为三个等级:

    • 入门级:主要考察基础算法与编程能力。
    • 工作级:更贴近实际业务需求,可能涉及复杂的算法或与工作内容相关的场景题目。
    • 专业级:最高等级,考察深层次的算法以及优化能力,与薪资直接挂钩。

刷题策略与说明:

2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:

  1. 关注历年真题:

    • 题库中的旧题占比较大,建议优先刷历年的A卷、B卷、C卷、D卷题目。
    • 对于每道题目,建议深度理解其解题思路、代码实现,以及相关算法的适用场景。
  2. 适应新题目:

    • E卷中包含全新题目,需要掌握全面的算法知识和一定的灵活应对能力。
    • 建议关注新的刷题平台或交流群,获取最新题目的解析和动态。
  3. 掌握常见算法:
    华为OD考试通常涉及以下算法和数据结构:

    • 排序算法(快速排序、归并排序等)
    • 动态规划(背包问题、最长公共子序列等)
    • 贪心算法
    • 栈、队列、链表的操作
    • 图论(最短路径、最小生成树等)
    • 滑动窗口、双指针算法
  4. 保持编程规范:

    • 注重代码的可读性和注释的清晰度。
    • 熟练使用常见编程语言,如C++、Java、Python等。

如何获取资源?

  1. 官方参考:

    • 华为招聘官网或相关的招聘平台会有一些参考信息。
    • 华为OD的相关公众号可能也会发布相关的刷题资料或学习资源。
  2. 加入刷题社区:

    • 找到可信的刷题交流群,与其他备考的小伙伴交流经验。
    • 关注知名的刷题网站,如LeetCode、牛客网等,这些平台上有许多华为OD的历年真题和解析。
  3. 寻找系统性的教程:

    • 学习一本经典的算法书籍,例如《算法导论》《剑指Offer》《编程之美》等。
    • 完成系统的学习课程,例如数据结构与算法的在线课程。

积极心态与持续努力:

刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。

考试注意细节

  1. 本地编写代码

    • 在本地 IDE(如 VS Code、PyCharm 等)上编写、保存和调试代码,确保逻辑正确后再复制粘贴到考试页面。这样可以减少语法错误,提高代码准确性。
  2. 调整心态,保持冷静

    • 遇到提示不足或实现不确定的问题时,不必慌张,可以采用更简单或更有把握的方法替代,确保思路清晰。
  3. 输入输出完整性

    • 注意训练和考试时都需要编写完整的输入输出代码,尤其是和题目示例保持一致。完成代码后务必及时调试,确保功能符合要求。
  4. 快捷键使用

    • 删除行可用 Ctrl+D,复制、粘贴和撤销分别为 Ctrl+CCtrl+VCtrl+Z,这些可以正常使用。
    • 避免使用 Ctrl+S,以免触发浏览器的保存功能。
  5. 浏览器要求

    • 使用最新版的 Google Chrome 浏览器完成考试,确保摄像头开启并正常工作。考试期间不要切换到其他网站,以免影响考试成绩。
  6. 交卷相关

    • 答题前,务必仔细查看题目示例,避免遗漏要求。
    • 每完成一道题后,点击【保存并调试】按钮,多次保存和调试是允许的,系统会记录得分最高的一次结果。完成所有题目后,点击【提交本题型】按钮。
    • 确保在考试结束前提交试卷,避免因未保存或调试失误而丢分。
  7. 时间和分数安排

    • 总时间:150 分钟;总分:400 分。
    • 试卷结构:2 道一星难度题(每题 100 分),1 道二星难度题(200 分)。及格分为 150 分。合理分配时间,优先完成自己擅长的题目。
  8. 考试环境准备

    • 考试前请备好草稿纸和笔。考试中尽量避免离开座位,确保监控画面正常。
    • 如需上厕所,请提前规划好时间以减少中途离开监控的可能性。
  9. 技术问题处理

    • 如果考试中遇到断电、断网、死机等技术问题,可以关闭浏览器并重新打开试卷链接继续作答。
    • 出现其他问题,请第一时间联系 HR 或监考人员进行反馈。

祝你考试顺利,取得理想成绩!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/67047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ-集群

RabbitMQ集群----主备关系&#xff0c;在运行的时候&#xff0c;如果非主要节点宕机&#xff0c;程序操作 不受影响&#xff1b; 如果主节点宕机了&#xff0c; 程序会中断操作。 而Rabbitmq集群&#xff0c;会马上让没有宕机的节点参选&#xff0c;选出新的主要节点。 程序重试…

postgresql分区表相关问题处理

1.使用pg_cron按日创建分区表&#xff0c;会出现所在数据库对应用户权限不足的问题。 原因是pg_cron运行在postgres数据库中&#xff0c;是用superuser进行执行的&#xff0c;对应的分区表的owner为postgres&#xff0c;所以需要单独授权对表的所有操作权限。不知道直接改变ow…

网络数据链路层以太网协议

网络数据链路层以太网协议 1. 以太网协议介绍 以太网是一个数据链路层协议&#xff0c;数据链路层的作用是用于两个设备&#xff08;同一种数据链路节点&#xff09;之间进行传递。 以太网不是一种具体的网络&#xff0c;而是一种网络技术标准&#xff0c;既包含了数据链路层…

Kotlin 循环语句详解

文章目录 循环类别for-in 循环区间整数区间示例1&#xff1a;正向遍历示例2&#xff1a;反向遍历 示例1&#xff1a;遍历数组示例2&#xff1a;遍历区间示例3&#xff1a;遍历字符串示例4&#xff1a;带索引遍历 while 循环示例&#xff1a;计算阶乘 do-while 循环示例&#xf…

【STM32】HAL库USB实现软件升级DFU的功能操作及配置

【STM32】HAL库USB实现软件升级DFU的功能操作及配置 文章目录 DFUHAL库的DFU配置修改代码添加条件判断和跳转代码段DFU烧录附录&#xff1a;Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作SysTick系统定时器精准延时延时函数阻塞延时非阻塞延时 位带操作位带代码位带宏…

使用WebdriverIO和Appium测试App

1.新建项目 打开Webstorm新建项目 打开终端输入命令 npm init -y npm install wdio/cli allure-commandline --save-dev npx wdio config 然后在终端依次选择如下&#xff1a; 然后在终端输入命令&#xff1a; npm install wdio/local-runnerlatest wdio/mocha-frameworkla…

uniapp小程序开发,配置开启小程序右上角三点的分享功能

直接全局配置一个分享的功能&#xff0c;要不然需要一个一个页面去单独配置就太麻烦了&#xff01;&#xff01;&#xff01; 1.新建一个share.js文件&#xff0c;并配置代码&#xff0c;调用onShareMessage()和onShareTimeline()两个函数。 其中&#xff1a; title&#xff1…

行业案例:高德服务单元化方案和架构实践

目录 为什么要做单元化 高德单元化的特点 高德单元化实践 服务单元化架构 就近接入实现方案 路由表设计 路由计算 服务端数据驱动的单元化场景 总结 系列阅读 为什么要做单元化 单机房资源瓶颈 随着业务体量和服务用户群体的增长,单机房或同城双机房无法支持服…

ip属地什么条件会改变?多角度深入探讨

IP属地&#xff0c;即IP地址的归属地&#xff0c;是互联网上设备连接时的一个关键信息&#xff0c;它通常反映了设备连接互联网时的地理位置。随着社交软件及各大平台推出IP归属地显示功能&#xff0c;IP属地的变化问题逐渐受到广大用户的关注。那么&#xff0c;IP属地在什么条…

【干货】交换网络环路介绍

定义 以太网交换网络中为了提高网络可靠性&#xff0c;通常会采用冗余设备和冗余链路&#xff0c;然而现网中由于组网调整、配置修改、升级割接等原因&#xff0c;经常会造成数据或协议报文环形转发&#xff0c;不可避免的形成环路。如图7-1所示&#xff0c;三台设备两两相连就…

Linux服务器网络丢包场景及解决办法

一、Linux网络丢包概述 在数字化浪潮席卷的当下&#xff0c;网络已然成为我们生活、工作与娱乐不可或缺的基础设施&#xff0c;如同空气般&#xff0c;无孔不入地渗透到各个角落。对于 Linux 系统的用户而言&#xff0c;网络丢包问题却宛如挥之不去的 “噩梦”&#xff0c;频繁…

Springboot内置Apache Tomcat 安全漏洞(CVE-2024-50379)

背景 大家都知道我们使用Springboot开发后&#xff0c;无需再额外配置tomcat&#xff0c;因为Springboot已经帮我们内置好了tomcat。 这次在线上安全团队就扫出来了我们Springboot服务的tomcat漏洞&#xff1a; 可以看到这是2023年的洞&#xff0c;Apache Tomcat 安全漏洞(…

杭州铭师堂的云原生升级实践

作者&#xff1a;升学e网通研发部基建团队 公司介绍 杭州铭师堂&#xff0c;是一个致力于为人的全面发展而服务的在线教育品牌。杭州铭师堂秉持“用互联网改变教育&#xff0c;让中国人都有好书读”的使命&#xff0c;致力于用“互联网教育”的科技手段让更多的孩子都能享有优…

qml XmlListModel详解

1、概述 XmlListModel是QtQuick用于从XML数据创建只读模型的组件。它可以作为各种view元素的数据源&#xff0c;比如ListView、GridView、PathView等&#xff1b;也可以作为其他和model交互的元素的数据源。通过XmlRole定义角色&#xff0c;如name、age和height&#xff0c;并…

《异步编程之美》— 全栈修仙《Java 8 CompletableFuture 对比 ES6 Promise 以及Spring @Async》

哈喽&#xff0c;大家好&#xff01;在平常开发过程中会遇到许多意想不到的坑&#xff0c;本篇文章就记录在开发过程中遇到一些常见的问题&#xff0c;看了许多博主的异步编程&#xff0c;我只能说一言难尽。本文详细的讲解了异步编程之美&#xff0c;是不可多得的好文&#xf…

《机器学习》之K-means聚类

目录 一、简介 二、K-means聚类实现步骤 1、初始化数据点、确定K值 2、通过距离分配数据点 3、更新簇中心 4、 迭代更新 三、聚类效果评价方式 1、轮廓系数的定义 2、整体轮廓系数 3、使用场景 4、优点 5、缺点 6、代码实现方法 四、K-means聚类代码实现 1、API接…

MVC执行流程

&#xff08;1&#xff09;用户通过浏览器&#xff08;客户端&#xff09;向服务端&#xff08;后端&#xff09;发送请求&#xff0c;请求会被前端控制器DispatcherServlet拦截。 &#xff08;2&#xff09;DispatcherServlet拦截到请求后&#xff0c;会调用处理器映射器&…

springboot和vue配置https请求

项目场景&#xff1a; 代码发布到线上使用https请求需要配置ssl证书&#xff0c;前后端都需要修改。 问题描述 如图&#xff0c;我们在调用接口时报如下错误&#xff0c;这就是未配置ssl但是用https请求产生的问题。 解决方案&#xff1a; 前端&#xff1a;在vite.config.js文…

单细胞组学大模型(8)--- scGenePT,scGPT和GenePT的结合,实验数据和文本数据的交融模型

–https://doi.org/10.1101/2024.10.23.619972 研究团队和单位 Theofanis Karaletsos–Head Of AI - Science at Chan Zuckerberg Initiative &#xff08;Chan Zuckerberg Initiative是扎克伯格和他妻子Chan成立的科研&教育机构&#xff09; 研究简介 研究背景&…

改进萤火虫算法之八:量子萤火虫算法(Quantum-behaved Firfly Algorithm,QFA)

量子萤火虫算法(Quantum-behaved Firfly Algorithm,QFA)是对萤火虫算法的一种改进,旨在提升萤火虫个体的搜索能力。以下是对量子萤火虫算法的详细介绍: 一、萤火虫算法基础 萤火虫算法(Firefly Algorithm,FA)是一种基于群体智能的优化算法,由剑桥大学的Xin-She Yang在…