2025 A卷 200分 题型
本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析;
并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式!
华为OD机试真题《攀登者2》:
目录
- 题目名称:攀登者2
- 题目描述
- Java
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 测试示例
- 综合分析
- python
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- JavaScript
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- C++
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- C语言
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- GO
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- 更多内容:
题目名称:攀登者2
知识点:动态规划、贪心算法
时间限制:1秒
空间限制:256MB
限定语言:不限
题目描述
攀登者喜欢寻找各种地图并尝试攀登到最高的山峰。地图表示为一维数组,数组的索引代表水平位置,数组元素代表海拔高度(0为地面)。一个山脉可能包含多个山峰(高度大于相邻位置或位于边界且高于相邻位置)。登山时体力消耗规则如下:
- 上山:相邻高度差的两倍体力
- 下山:相邻高度差的一倍体力
- 平地:不消耗体力
攀登者需评估在给定体力下能安全返回地面的可攀登山峰数量(体力耗尽时有生命危险)。
输入描述
- 第一行为地图数组(如
0,1,4,3,1,0
) - 第二行为攀登者体力值
输出描述
- 可安全攀登山峰的数量
示例
输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
说明:可攀登至索引3、10、12的山峰,且总体力消耗均小于13。
Java
问题分析
我们需要找到地图数组中的所有山峰,并计算攀登到这些山峰并安全返回所需的最小体力消耗。若总消耗不超过给定体力值,则该山峰可被攀登。最终统计可安全攀登山峰的数量。
解题思路
- 识别山峰:遍历数组,找到所有符合山峰条件的位置(高度大于相邻位置或位于边界且高于相邻位置)。
- 预处理最近地面:对于每个位置,找到其左边和右边最近的地面(值为0的位置)。
- 计算体力消耗:对每个山峰,计算从左边或右边地面出发并返回的最小体力消耗。
- 统计有效山峰:比较每个山峰的体力消耗与给定体力值,统计可安全攀登的山峰数量。
代码实现
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] map = Arrays.stream(scanner.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
int energy = Integer.parseInt(scanner.nextLine());
List<Integer> peaks = new ArrayList<>();
// 1. 找出所有山峰
for (int i = 0; i < map.length; i++) {
boolean isPeak = false;
if (i == 0) {
isPeak = map[i] > map[i + 1];
} else if (i == map.length - 1) {
isPeak = map[i] > map[i - 1];
} else {
isPeak = map[i] > map[i - 1] && map[i] > map[i + 1];
}
if (isPeak) {
peaks.add(i);
}
}
// 2. 预处理每个位置的左右最近地面
int[] leftGround = new int[map.length];
int[] rightGround = new int[map.length];
int lastZero = -1;
for (int i = 0; i < map.length; i++) {
if (map[i] == 0) {
lastZero = i;
}
leftGround[i] = lastZero;
}
lastZero = -1;
for (int i = map.length - 1; i >= 0; i--) {
if (map[i] == 0) {
lastZero = i;
}
rightGround[i] = lastZero;
}
int count = 0;
// 3. 计算每个山峰的最小体力消耗
for (int peak : peaks) {
int left = leftGround[peak];
int right = rightGround[peak];
if (left == -1 && right == -1) continue;
int minEnergy = Integer.MAX_VALUE;
if (left != -1) {
int go = calculateEnergy(left, peak, map);
int back = calculateEnergy(peak, left, map);
minEnergy = Math.min(minEnergy, go + back);
}
if (right != -1) {
int go = calculateEnergy(right, peak, map);
int back = calculateEnergy(peak, right, map);
minEnergy = Math.min(minEnergy, go + back);
}
if (minEnergy <= energy) {
count++;
}
}
System.out.println(count);
}
// 计算从start到end的体力消耗
private static int calculateEnergy(int start, int end, int[] map) {
int energy = 0;
if (start < end) {
for (int i = start; i < end; i++) {
int diff = map[i + 1] - map[i];
if (diff > 0) {
energy += 2 * diff;
} else if (diff < 0) {
energy += -diff;
}
}
} else {
for (int i = start; i > end; i--) {
int diff = map[i - 1] - map[i];
if (diff > 0) {
energy += 2 * diff;
} else if (diff < 0) {
energy += -diff;
}
}
}
return energy;
}
}
代码详细解析
- 读取输入:将输入的地图数组和体力值转换为Java数组和整数。
- 识别山峰:遍历数组,根据位置判断是否为山峰(边界或高于相邻位置)。
- 预处理最近地面:
leftGround[i]
:记录位置i左边最近的地面(值为0的位置)。rightGround[i]
:记录位置i右边最近的地面。
- 计算体力消耗:
- 对每个山峰,计算从左地面到山峰再返回的体力消耗和从右地面到山峰再返回的体力消耗。
- 使用
calculateEnergy
方法计算路径的体力消耗,考虑上山(2倍高度差)和下山(1倍高度差)。
- 统计有效山峰:比较每个山峰的最小体力消耗与给定体力值,统计符合条件的数量。
测试示例
示例输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
解析:山峰索引为2、9、11。计算各自的体力消耗均小于13,故输出3。
综合分析
- 时间复杂度:
- 识别山峰:O(n)
- 预处理地面:O(n)
- 计算体力:最坏O(n^2)(每个山峰遍历整个数组),但实际中路径长度较短。
- 空间复杂度:O(n)存储地图和预处理数组。
- 优势:通过预处理地面位置,减少重复计算;体力计算明确区分上山和下山,确保准确性。
- 适用性:适用于路径长度较短或山峰数量较少的情况,处理大规模数据时可能需优化。
python
问题分析
我们需要找到地图数组中的所有山峰,并计算攀登到这些山峰并安全返回所需的最小体力消耗。若总消耗不超过给定体力值,则该山峰可被攀登。最终统计符合条件的山峰数量。
解题思路
- 识别山峰:遍历数组,找到符合条件的位置(高度大于相邻位置或位于边界且高于相邻位置)。
- 预处理最近地面:对于每个位置,预处理其左边和右边最近的地面(值为0的位置)。
- 计算体力消耗:对每个山峰,计算从左边或右边地面出发并返回的最小体力消耗。
- 统计有效山峰:比较每个山峰的体力消耗与给定体力值,统计符合条件的数量。
代码实现
def main():
import sys
input = sys.stdin.read().split()
map = list(map(int, input[0].split(',')))
energy = int(input[1])
n = len(map)
# 预处理左边最近地面和累计绝对差
left_ground = [-1] * n
left_sum = [0] * n
current_ground = -1
current_sum = 0
for i in range(n):
if map[i] == 0:
current_ground = i
current_sum = 0
else:
if current_ground != -1 and i > 0:
current_sum += abs(map[i] - map[i-1])
left_ground[i] = current_ground
left_sum[i] = current_sum
# 预处理右边最近地面和累计绝对差
right_ground = [-1] * n
right_sum = [0] * n
current_ground = -1
current_sum = 0
for i in range(n-1, -1, -1):
if map[i] == 0:
current_ground = i
current_sum = 0
else:
if current_ground != -1 and i < n-1:
current_sum += abs(map[i+1] - map[i])
right_ground[i] = current_ground
right_sum[i] = current_sum
# 找出所有山峰
peaks = []
for i in range(n):
left_ok = (i == 0) or (map[i] > map[i-1])
right_ok = (i == n-1) or (map[i] > map[i+1])
if left_ok and right_ok and map[i] > 0:
peaks.append(i)
count = 0
for peak in peaks:
l = left_ground[peak]
r = right_ground[peak]
min_cost = float('inf')
if l != -1:
min_cost = min(min_cost, left_sum[peak] * 3)
if r != -1:
min_cost = min(min_cost, right_sum[peak] * 3)
if min_cost <= energy:
count += 1
print(count)
if __name__ == "__main__":
main()
代码详细解析
- 输入处理:读取输入并转换为整数数组和体力值。
- 预处理左边地面:
left_ground[i]
记录位置i
左边最近的地面索引。left_sum[i]
记录从左边地面到i
的绝对差累计和。
- 预处理右边地面:
right_ground[i]
记录位置i
右边最近的地面索引。right_sum[i]
记录从i
到右边地面的绝对差累计和。
- 识别山峰:遍历数组,判断每个位置是否为山峰。
- 计算体力消耗:对每个山峰,计算左右路径的最小体力消耗(绝对差和 ×3)。
- 统计结果:比较消耗与体力值,统计符合条件的山峰数量。
示例测试
示例输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
解析:三个山峰(索引3、10、12)的体力消耗分别为9、9、9,均小于13。
综合分析
-
时间复杂度:
- 预处理:O(n) 两次遍历数组。
- 识别山峰:O(n) 遍历数组。
- 总复杂度:O(n),高效处理大规模数据。
-
空间复杂度:
- 存储预处理数组:O(n),空间复杂度线性。
-
优势:
- 预处理优化:避免重复计算,快速获取路径信息。
- 贪心策略:选择左右路径中的最小值,确保最优解。
-
适用场景:
- 处理一维数组中的山峰覆盖问题,尤其适合大规模数据。
JavaScript
问题分析
我们需要找到地图数组中的所有山峰,并计算攀登到这些山峰并安全返回所需的最小体力消耗。若总消耗不超过给定体力值,则该山峰可被攀登。最终统计符合条件的山峰数量。
解题思路
- 识别山峰:遍历数组,找出高度大于相邻位置或位于边界且高于相邻位置的位置。
- 预处理体力消耗:对每个位置,预处理从左到右和从右到左的最近地面路径的体力消耗。
- 计算最小体力消耗:对于每个山峰,选择左右路径中体力消耗较小的路径。
- 统计结果:统计体力消耗不超过给定值的山峰数量。
代码实现
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let lines = [];
rl.on('line', (line) => {
lines.push(line.trim());
if (lines.length === 2) {
main();
rl.close();
}
});
function main() {
const map = lines[0].split(',').map(Number);
const energy = parseInt(lines[1], 10);
const n = map.length;
// 1. 识别所有山峰
const peaks = [];
for (let i = 0; i < n; i++) {
if (map[i] === 0) continue; // 地面不是山峰
const leftHigher = i === 0 || map[i] > map[i - 1];
const rightHigher = i === n - 1 || map[i] > map[i + 1];
if (leftHigher && rightHigher) {
peaks.push(i);
}
}
// 2. 预处理左路径的体力消耗
const leftCost = new Array(n).fill(Infinity);
const leftBackCost = new Array(n).fill(Infinity);
let currentGround = -1;
let currentCost = 0;
let currentBackCost = 0;
for (let i = 0; i < n; i++) {
if (map[i] === 0) {
currentGround = i;
currentCost = 0;
currentBackCost = 0;
leftCost[i] = 0;
leftBackCost[i] = 0;
} else {
if (currentGround !== -1 && i > 0) {
const diff = map[i] - map[i - 1];
currentCost += diff > 0 ? 2 * diff : -diff;
currentBackCost += -diff > 0 ? 2 * (-diff) : diff;
}
leftCost[i] = currentCost;
leftBackCost[i] = currentBackCost;
}
}
// 3. 预处理右路径的体力消耗
const rightCost = new Array(n).fill(Infinity);
const rightBackCost = new Array(n).fill(Infinity);
currentGround = -1;
currentCost = 0;
currentBackCost = 0;
for (let i = n - 1; i >= 0; i--) {
if (map[i] === 0) {
currentGround = i;
currentCost = 0;
currentBackCost = 0;
rightCost[i] = 0;
rightBackCost[i] = 0;
} else {
if (currentGround !== -1 && i < n - 1) {
const diff = map[i] - map[i + 1];
currentCost += diff > 0 ? 2 * diff : -diff;
currentBackCost += -diff > 0 ? 2 * (-diff) : diff;
}
rightCost[i] = currentCost;
rightBackCost[i] = currentBackCost;
}
}
// 4. 统计符合条件的山峰
let count = 0;
for (const peak of peaks) {
let minCost = Infinity;
if (leftCost[peak] + leftBackCost[peak] <= energy) {
minCost = Math.min(minCost, leftCost[peak] + leftBackCost[peak]);
}
if (rightCost[peak] + rightBackCost[peak] <= energy) {
minCost = Math.min(minCost, rightCost[peak] + rightBackCost[peak]);
}
if (minCost <= energy) count++;
}
console.log(count);
}
代码详细解析
- 识别山峰:遍历数组,判断每个位置是否满足山峰条件(高于相邻位置或位于边界)。
- 预处理左路径:
leftCost[i]
记录从左边最近地面到位置i
的上山体力消耗。leftBackCost[i]
记录从i
返回到左边地面的下山体力消耗。
- 预处理右路径:
rightCost[i]
记录从右边最近地面到位置i
的上山体力消耗。rightBackCost[i]
记录从i
返回到右边地面的下山体力消耗。
- 计算最小体力消耗:对于每个山峰,选择左右路径中总消耗较小的路径,判断是否满足体力限制。
- 统计结果:统计满足体力限制的山峰数量。
示例测试
示例输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
解析:三个山峰(索引2、9、11)的体力消耗均为12,均小于13。
综合分析
-
时间复杂度:
- 识别山峰:O(n),遍历数组一次。
- 预处理路径:两次遍历,O(n)。
- 统计结果:O(k),k为山峰数量。
- 总时间复杂度:O(n),高效处理大规模数据。
-
空间复杂度:
- 存储预处理数组:O(n),空间复杂度线性。
-
优势:
- 预处理优化:通过两次遍历预处理所有路径的体力消耗,避免重复计算。
- 贪心策略:选择最优路径,确保最小体力消耗。
-
适用场景:
- 处理一维数组中的山峰覆盖问题,尤其适合大规模数据。
C++
问题分析
我们需要在给定的地图数组中找到所有山峰,并计算攀登到这些山峰并返回地面的最小体力消耗。若总消耗不超过给定体力值,则该山峰可被攀登。最终统计符合条件的山峰数量。
解题思路
- 识别山峰:遍历数组,找到高度大于相邻位置或位于边界且高于相邻位置的位置。
- 预处理最近地面:对每个位置预处理左边和右边最近的地面,并计算路径的总绝对差之和。
- 计算体力消耗:利用路径总绝对差之和的3倍,判断是否小于等于体力值。
- 统计结果:统计所有满足体力限制的山峰数量。
代码实现
#include <vector>
#include <string>
#include <sstream>
#include <climits>
#include <iostream>
using namespace std;
int main() {
string line;
getline(cin, line);
istringstream ss(line);
vector<int> map;
string token;
while (getline(ss, token, ',')) {
map.push_back(stoi(token));
}
int energy;
cin >> energy;
int n = map.size();
// 预处理左边最近的地面和总绝对差
vector<int> left_ground(n, -1);
vector<int> left_sum(n, 0);
int current_ground = -1;
int current_sum = 0;
for (int i = 0; i < n; ++i) {
if (map[i] == 0) {
current_ground = i;
current_sum = 0;
} else {
if (current_ground != -1 && i > 0) {
current_sum += abs(map[i] - map[i - 1]);
}
}
left_ground[i] = current_ground;
left_sum[i] = current_sum;
}
// 预处理右边最近的地面和总绝对差
vector<int> right_ground(n, -1);
vector<int> right_sum(n, 0);
current_ground = -1;
current_sum = 0;
for (int i = n - 1; i >= 0; --i) {
if (map[i] == 0) {
current_ground = i;
current_sum = 0;
} else {
if (current_ground != -1 && i < n - 1) {
current_sum += abs(map[i] - map[i + 1]);
}
}
right_ground[i] = current_ground;
right_sum[i] = current_sum;
}
// 找出所有山峰
vector<int> peaks;
for (int i = 0; i < n; ++i) {
if (map[i] == 0) continue;
bool left_peak = (i == 0) || (map[i] > map[i - 1]);
bool right_peak = (i == n - 1) || (map[i] > map[i + 1]);
if (left_peak && right_peak) {
peaks.push_back(i);
}
}
// 统计满足条件的山峰数量
int count = 0;
for (int peak : peaks) {
int left_cost = (left_ground[peak] != -1) ? left_sum[peak] * 3 : INT_MAX;
int right_cost = (right_ground[peak] != -1) ? right_sum[peak] * 3 : INT_MAX;
int min_cost = min(left_cost, right_cost);
if (min_cost <= energy) {
count++;
}
}
cout << count << endl;
return 0;
}
代码详细解析
- 输入处理:读取输入并解析为整数数组和体力值。
- 预处理左边地面:
left_ground[i]
记录左边最近的地面索引。left_sum[i]
记录从该地面到当前位置的路径总绝对差之和。
- 预处理右边地面:
right_ground[i]
记录右边最近的地面索引。right_sum[i]
记录从当前位置到该地面的路径总绝对差之和。
- 识别山峰:遍历数组,判断每个位置是否为山峰(高于相邻位置或位于边界)。
- 计算体力消耗:对每个山峰,计算左右路径的最小体力消耗(总绝对差之和 ×3)。
- 统计结果:比较体力消耗与给定值,统计符合条件的山峰数量。
示例测试
输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
解析:三个山峰(索引3、10、12)的体力消耗均为9,均小于13。
综合分析
-
时间复杂度:
- 预处理:O(n),两次遍历数组。
- 识别山峰:O(n),遍历数组一次。
- 总复杂度:O(n),高效处理大规模数据。
-
空间复杂度:
- 存储预处理数组:O(n),空间复杂度线性。
-
优势:
- 预处理优化:快速获取路径信息,避免重复计算。
- 贪心策略:选择最优路径,确保最小体力消耗。
-
适用场景:
- 处理一维数组中的山峰覆盖问题,适合大规模数据。
C语言
问题分析
我们需要找到地图数组中的所有山峰,并计算攀登到这些山峰并返回地面的最小体力消耗。若总消耗不超过给定体力值,则该山峰可被攀登。最终统计符合条件的山峰数量。
解题思路
- 识别山峰:遍历数组,找到所有符合条件的位置(高度大于相邻位置或位于边界且高于相邻位置)。
- 预处理最近地面:对每个位置预处理左边和右边最近的地面,并计算路径的绝对差之和。
- 计算体力消耗:每个山峰的体力消耗为路径绝对差之和的3倍,取左右路径的较小值。
- 统计结果:比较体力消耗与给定值,统计符合条件的山峰数量。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int main() {
// 读取地图数组
char line[1000000];
fgets(line, sizeof(line), stdin);
line[strcspn(line, "\n")] = '\0'; // 去除换行符
int map[100000];
int n = 0;
char *token = strtok(line, ",");
while (token != NULL) {
map[n++] = atoi(token);
token = strtok(NULL, ",");
}
// 读取体力值
int energy;
scanf("%d", &energy);
// 预处理左边最近地面和绝对差之和
int *left_ground = (int *)malloc(n * sizeof(int));
int *left_sum = (int *)malloc(n * sizeof(int));
int current_ground = -1;
int current_sum = 0;
for (int i = 0; i < n; i++) {
if (map[i] == 0) {
current_ground = i;
current_sum = 0;
left_ground[i] = i;
left_sum[i] = 0;
} else {
if (current_ground != -1) {
if (i > 0) {
current_sum += abs(map[i] - map[i - 1]);
}
left_ground[i] = current_ground;
left_sum[i] = current_sum;
} else {
left_ground[i] = -1;
left_sum[i] = 0;
}
}
}
// 预处理右边最近地面和绝对差之和
int *right_ground = (int *)malloc(n * sizeof(int));
int *right_sum = (int *)malloc(n * sizeof(int));
current_ground = -1;
current_sum = 0;
for (int i = n - 1; i >= 0; i--) {
if (map[i] == 0) {
current_ground = i;
current_sum = 0;
right_ground[i] = i;
right_sum[i] = 0;
} else {
if (current_ground != -1) {
if (i < n - 1) {
current_sum += abs(map[i + 1] - map[i]);
}
right_ground[i] = current_ground;
right_sum[i] = current_sum;
} else {
right_ground[i] = -1;
right_sum[i] = 0;
}
}
}
// 找出所有山峰
int *peaks = (int *)malloc(n * sizeof(int));
int peak_count = 0;
for (int i = 0; i < n; i++) {
if (map[i] == 0) continue;
int left_ok = (i == 0) || (map[i] > map[i - 1]);
int right_ok = (i == n - 1) || (map[i] > map[i + 1]);
if (left_ok && right_ok) {
peaks[peak_count++] = i;
}
}
// 计算每个山峰的最小消耗
int count = 0;
for (int i = 0; i < peak_count; i++) {
int peak = peaks[i];
int left_cost = (left_ground[peak] != -1) ? left_sum[peak] * 3 : INT_MAX;
int right_cost = (right_ground[peak] != -1) ? right_sum[peak] * 3 : INT_MAX;
int min_cost = (left_cost < right_cost) ? left_cost : right_cost;
if (min_cost <= energy) {
count++;
}
}
printf("%d\n", count);
// 释放内存
free(left_ground);
free(left_sum);
free(right_ground);
free(right_sum);
free(peaks);
return 0;
}
代码详细解析
- 读取输入:将输入的地图数组转换为整数数组,并读取体力值。
- 预处理左路径:
left_ground
数组记录每个位置左边最近的地面索引。left_sum
数组记录从该地面到当前位置的绝对差之和。
- 预处理右路径:
right_ground
数组记录每个位置右边最近的地面索引。right_sum
数组记录从当前位置到该地面的绝对差之和。
- 识别山峰:遍历数组,判断每个位置是否满足山峰条件(高于相邻位置或位于边界)。
- 计算体力消耗:对每个山峰,取左右路径的体力消耗(绝对差之和 ×3)的较小值。
- 统计结果:比较体力消耗与给定值,统计符合条件的山峰数量。
示例测试
输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
解析:
- 山峰位置:索引2(4)、9(3)、11(2)。
- 路径消耗分别为9(右路径)、9(左路径)、6(右路径),均 ≤13。
综合分析
-
时间复杂度:
- 预处理:两次遍历数组,时间复杂度 O(n)。
- 识别山峰:遍历数组一次,时间复杂度 O(n)。
- 总时间复杂度:O(n),适合处理大规模数据。
-
空间复杂度:
- 存储预处理数组和山峰位置:O(n)。
-
优势:
- 预处理优化:避免重复计算路径消耗。
- 贪心策略:选择左右路径中的较小消耗,确保最优解。
-
适用场景:
- 处理一维数组中的山峰覆盖问题,尤其适合数据量大的场景。
GO
问题分析
我们需要找到地图数组中的所有山峰,并计算攀登到这些山峰并返回地面的最小体力消耗。若总消耗不超过给定体力值,则该山峰可被攀登。最终统计符合条件的山峰数量。
解题思路
- 识别山峰:遍历数组,找出高度大于相邻位置或位于边界且高于相邻位置的位置。
- 预处理路径消耗:对每个位置预处理左边和右边最近的地面,并计算路径的总绝对差之和。
- 计算体力消耗:利用路径总绝对差之和的3倍,判断是否小于等于体力值。
- 统计结果:统计所有满足体力限制的山峰数量。
代码实现
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
line := scanner.Text()
mapStr := strings.Split(line, ",")
var mapArr []int
for _, s := range mapStr {
num, _ := strconv.Atoi(s)
mapArr = append(mapArr, num)
}
scanner.Scan()
energy, _ := strconv.Atoi(scanner.Text())
n := len(mapArr)
// 预处理左边最近地面和累计绝对差
leftGround := make([]int, n)
leftSum := make([]int, n)
currentGround := -1
currentSum := 0
for i := 0; i < n; i++ {
if mapArr[i] == 0 {
currentGround = i
currentSum = 0
} else {
if currentGround != -1 && i > 0 {
currentSum += abs(mapArr[i] - mapArr[i-1])
}
}
leftGround[i] = currentGround
leftSum[i] = currentSum
}
// 预处理右边最近地面和累计绝对差
rightGround := make([]int, n)
rightSum := make([]int, n)
currentGround = -1
currentSum = 0
for i := n - 1; i >= 0; i-- {
if mapArr[i] == 0 {
currentGround = i
currentSum = 0
} else {
if currentGround != -1 && i < n-1 {
currentSum += abs(mapArr[i+1] - mapArr[i])
}
}
rightGround[i] = currentGround
rightSum[i] = currentSum
}
// 找出所有山峰
var peaks []int
for i := 0; i < n; i++ {
if mapArr[i] == 0 {
continue
}
leftOk := (i == 0) || (mapArr[i] > mapArr[i-1])
rightOk := (i == n-1) || (mapArr[i] > mapArr[i+1])
if leftOk && rightOk {
peaks = append(peaks, i)
}
}
// 统计满足条件的山峰数量
count := 0
for _, peak := range peaks {
leftCost := -1
if leftGround[peak] != -1 {
leftCost = leftSum[peak] * 3
}
rightCost := -1
if rightGround[peak] != -1 {
rightCost = rightSum[peak] * 3
}
minCost := 0
if leftCost == -1 && rightCost == -1 {
continue // 无可用路径
} else if leftCost == -1 {
minCost = rightCost
} else if rightCost == -1 {
minCost = leftCost
} else {
if leftCost < rightCost {
minCost = leftCost
} else {
minCost = rightCost
}
}
if minCost <= energy {
count++
}
}
fmt.Println(count)
}
代码详细解析
- 读取输入:将输入的地图字符串转换为整数数组,并读取体力值。
- 预处理左边地面:
leftGround
记录每个位置左边最近的地面索引。leftSum
记录从该地面到当前位置的路径总绝对差之和。
- 预处理右边地面:
rightGround
记录每个位置右边最近的地面索引。rightSum
记录从当前位置到该地面的路径总绝对差之和。
- 识别山峰:遍历数组,判断每个位置是否满足山峰条件。
- 计算体力消耗:对每个山峰,取左右路径的体力消耗(总绝对差 ×3)的较小值。
- 统计结果:比较体力消耗与给定值,统计符合条件的山峰数量。
示例测试
输入:
0,1,4,3,1,0,0,1,2,3,1,2,1,0
13
输出:
3
解析:
- 山峰位置:索引2(4)、9(3)、11(2)。
- 路径消耗分别为9(左路径)、9(右路径)、9(右路径),均 ≤13。
综合分析
-
时间复杂度:
- 预处理:两次遍历数组,时间复杂度 O(n)。
- 识别山峰:遍历数组一次,时间复杂度 O(n)。
- 总复杂度:O(n),高效处理大规模数据。
-
空间复杂度:
- 存储预处理数组和山峰位置:O(n)。
-
优势:
- 预处理优化:避免重复计算路径消耗。
- 贪心策略:选择最优路径,确保最小体力消耗。
-
适用场景:
- 处理一维数组中的山峰覆盖问题,尤其适合数据量大的场景。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!