/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
山区救援路线规划(Java/Py/C/C++/Js/Go)题解
华为OD机试真题 新系统 华为OD上机考试真题新系统 11月1号 200分题型
华为OD机试新系统真题目录点击查看: 华为OD机试新系统真题题库目录|机考题库 + 算法考点详解
题目内容
某山区建立了一张应急救援路网,路网中共有n个路口,编号为1~n,每个路口都有各自的海拔高度(米)。救援队从1号路口出发,每到一个路口,只能继续走向它的左路口或右路口,直到走到左右两侧都无路可走的路口(终点)为止,从1号路口走到某个终点的行进路径,称为一条”路线”。
救援队需要权衡两方面因素:
- 每走一段路,如果海拔升高(爬升),这一段的能耗为α×高差;如果海拔降低(下降),这一段的能耗为β×高差,这里的高差取绝对值。一条路线的总能耗为各段能耗之和。
- 海拔极差:一条路线经过的全部路口(包含1号路口)中,最高海拔与最低海拔的差值不能超过安全阀值,否 则气压变化过大,队员有高原反应风险。
你的任务是:在所有海拔极差不超过的路线中,找出总能耗最小的路线。若存在多条能耗相同的路线,选择路口编号序列字典序较小的那条:按顺序逐个比较两条序列的元素,第一个不同的位置上元素更小者更小:若一条序列是另一条序列的前缀,则较短的序列更小。
路网保证以1号路口为根的二叉树
输入描述
n: 路口数量(3 <= n <= 1000)
α 和 β:上坡体力消耗权重,下降体力消耗权重。范围为[1,10]
s: 海拔极差阈值(1 <= s <= 4000)
接下来n行每行包含4个整数,分别表示
id value left right
- id:路口编号
- value:海拔高度(0 <= value <= 10^4)
- left: 左路口编号,没有则为-1
- right:右路口编号,没有则为-1
输出描述
返回总能耗最小的路线。若存在多条能耗相同的路线,选择路口编号序列字典序较小的那条。如果没有任何线路满足要求,返回空。
样例1
输入
7
4 2
500
1 1200 2 3
2 1000 4 5
3 1500 6 7
4 1250 -1 -1
5 600 -1 -1
6 1100 -1 -1
7 2000 -1 -1
输出
1 2 4
说明
共有四条线路:1,2,4 1,2,5 1,3,6 1,3,7
1,2,5 和 1,3,7不满足极差分别为600 800
1,2,4极差为250,能耗为1400
1,3,6极差为400,能耗为2000
因此返回1,2,4
样例2
输入
3
2 2
300
1 500 2 3
2 700 -1 -1
3 300 -1 -1
输出
1 2
解题思路
思路:递归回溯 + 剪枝
从根节点 1 出发,一直走到叶子节点,通过 DFS 枚举所有根到叶子的路径,同时维护路径上的最高海拔、最低海拔和累计能耗。
递归回溯过程中维持以下状态, 并记录已经收集到的最佳路径以及对应能耗
- u:当前节点
- maxH:当前路径最高海拔
- minH: 当前路径最低海拔
- cost : 当前路径累计能耗
- path: 当前路径的节点编号序列
递归每次访问新节点时
- 更新路径最高海拔和最低海拔
- 判断海拔极差是否超过阈值。如果超过,直接剪枝。
- 计算当前边的能耗。如果当前路径总能耗超过当前最佳路径能耗直接剪枝。
- 如果是叶子节点,和当前最后路径进行比较尝试更新最优答案。否则继续递归左右子树。
C++
#include <bits/stdc++.h>
#include <vector>
using namespace std;
using ll = long long;
struct Node {
int value;
int left, right;
};
vector<Node> tree;
ll bestCost = LLONG_MAX;
vector<int> bestPath;
vector<int> path;
// 比较两条路线,选择字典序较小的路线
bool smaller(vector<int>& a, vector<int>& b) {
int len = min(a.size(), b.size());
for (int i = 0; i < len; i++) {
if (a[i] != b[i]) {
return a[i] < b[i];
}
}
return a.size() < b.size();
}
// DFS 搜索所有合法路线
void dfs(int u, int maxH, int minH, ll cost, int a, int b, int s) {
// 更新当前路径的最高和最低海拔
maxH = max(maxH, tree[u].value);
minH = min(minH, tree[u].value);
// 海拔极差超过阈值,直接剪枝
if (maxH – minH > s) {
return;
}
// 当前能耗已经超过最优答案,直接剪枝
if (cost > bestCost) {
return;
}
path.push_back(u);
// 到达叶子节点
if (tree[u].left == –1 && tree[u].right == –1) {
if (cost < bestCost ||
(cost == bestCost && (bestPath.empty() || smaller(path, bestPath)))) {
bestCost = cost;
bestPath = path;
}
path.pop_back();
return;
}
// 计算下一条边的能耗
auto getCost = [&](int v) -> ll {
int diff = tree[v].value – tree[u].value;
if (diff > 0) {
return a * diff;
}
return b * (–diff);
};
// 继续走向左子节点
if (tree[u].left != –1) {
int v = tree[u].left;
dfs(v, maxH, minH, cost + getCost(v), a, b, s);
}
// 继续走向右子节点
if (tree[u].right != –1) {
int v = tree[u].right;
dfs(v, maxH, minH, cost + getCost(v), a, b, s);
}
path.pop_back();
}
vector<int> solve(int n, int a, int b, int s, vector<vector<int>>& node) {
tree.resize(n + 1);
for (int i = 0; i < n; i++) {
int id, value, left, right;
id = node[i][0];
value = node[i][1];
left = node[i][2];
right = node[i][3];
tree[id] = {value, left, right};
}
// 从根节点 1 开始搜索
dfs(1, tree[1].value, tree[1].value, 0, a, b, s);
return bestPath;
}
int main() {
int n,a,b,s;
cin >> n;
cin >> a >> b;
cin >>s;
vector<vector<int>> node(n);
for (int i = 0; i < n; i++) {
int id, value, left, right;
cin >> id >> value >> left >> right;
node[i] = {id, value, left, right};
}
vector<int> ans = solve(n, a, b, s, node);
// 没有合法路线
if (ans.empty()) {
cout << "";
return 0;
}
// 输出最优路线
for (int i = 0; i < ans.size(); i++) {
if (i > 0) cout << " ";
cout << ans[i];
}
return 0;
}
JAVA
import java.util.*;
public class Main {
static class Node {
int value;
int left, right;
Node(int value, int left, int right) {
this.value = value;
this.left = left;
this.right = right;
}
}
static Node[] tree;
static long bestCost = Long.MAX_VALUE;
static List<Integer> bestPath = new ArrayList<>();
static List<Integer> path = new ArrayList<>();
// 比较两条路线,选择字典序较小的路线
static boolean smaller(List<Integer> a, List<Integer> b) {
int len = Math.min(a.size(), b.size());
for (int i = 0; i < len; i++) {
if (!a.get(i).equals(b.get(i))) {
return a.get(i) < b.get(i);
}
}
return a.size() < b.size();
}
// DFS 搜索所有合法路线
static void dfs(int u, int maxH, int minH, long cost, int a, int b, int s) {
// 更新当前路径的最高和最低海拔
maxH = Math.max(maxH, tree[u].value);
minH = Math.min(minH, tree[u].value);
// 海拔极差超过阈值,直接剪枝
if (maxH – minH > s) {
return;
}
// 当前能耗已经超过最优答案,直接剪枝
if (cost > bestCost) {
return;
}
path.add(u);
// 到达叶子节点
if (tree[u].left == –1 && tree[u].right == –1) {
if (cost < bestCost ||
(cost == bestCost && (bestPath.isEmpty() || smaller(path, bestPath)))) {
bestCost = cost;
bestPath = new ArrayList<>(path);
}
path.remove(path.size() – 1);
return;
}
// 计算下一条边的能耗
if (tree[u].left != –1) {
int v = tree[u].left;
int diff = tree[v].value – tree[u].value;
long nextCost = diff > 0 ? (long) a * diff : (long) b * (–diff);
// 继续走向左子节点
dfs(v, maxH, minH, cost + nextCost, a, b, s);
}
if (tree[u].right != –1) {
int v = tree[u].right;
int diff = tree[v].value – tree[u].value;
long nextCost = diff > 0 ? (long) a * diff : (long) b * (–diff);
// 继续走向右子节点
dfs(v, maxH, minH, cost + nextCost, a, b, s);
}
path.remove(path.size() – 1);
}
static List<Integer> solve(int n, int a, int b, int s, int[][] node) {
tree = new Node[n + 1];
bestCost = Long.MAX_VALUE;
bestPath = new ArrayList<>();
path = new ArrayList<>();
for (int i = 0; i < n; i++) {
int id = node[i][0];
int value = node[i][1];
int left = node[i][2];
int right = node[i][3];
tree[id] = new Node(value, left, right);
}
// 从根节点 1 开始搜索
dfs(1, tree[1].value, tree[1].value, 0, a, b, s);
return bestPath;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a = sc.nextInt();
int b = sc.nextInt();
int s = sc.nextInt();
int[][] node = new int[n][4];
for (int i = 0; i < n; i++) {
for (int j = 0; j < 4; j++) {
node[i][j] = sc.nextInt();
}
}
List<Integer> ans = solve(n, a, b, s, node);
// 没有合法路线
if (ans.isEmpty()) {
System.out.println("");
return;
}
// 输出最优路线
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ans.size(); i++) {
if (i > 0) sb.append(" ");
sb.append(ans.get(i));
}
System.out.println(sb);
}
}
Python
import sys
class Node:
def __init__(self, value, left, right):
self.value = value
self.left = left
self.right = right
bestCost = float('inf')
bestPath = []
path = []
tree = []
# 比较两条路线,选择字典序较小的路线
def smaller(a, b):
length = min(len(a), len(b))
for i in range(length):
if a[i] != b[i]:
return a[i] < b[i]
return len(a) < len(b)
# DFS 搜索所有合法路线
def dfs(u, maxH, minH, cost, a, b, s):
global bestCost, bestPath
# 更新当前路径的最高和最低海拔
maxH = max(maxH, tree[u].value)
minH = min(minH, tree[u].value)
# 海拔极差超过阈值,直接剪枝
if maxH – minH > s:
return
# 当前能耗已经超过最优答案,直接剪枝
if cost > bestCost:
return
path.append(u)
# 到达叶子节点
if tree[u].left == –1 and tree[u].right == –1:
if cost < bestCost or (
cost == bestCost and (not bestPath or smaller(path, bestPath))
):
bestCost = cost
bestPath = path[:]
path.pop()
return
# 计算下一条边的能耗
# 继续走向左子节点
if tree[u].left != –1:
v = tree[u].left
diff = tree[v].value – tree[u].value
nextCost = a * diff if diff > 0 else b * (–diff)
dfs(v, maxH, minH, cost + nextCost, a, b, s)
# 继续走向右子节点
if tree[u].right != –1:
v = tree[u].right
diff = tree[v].value – tree[u].value
nextCost = a * diff if diff > 0 else b * (–diff)
dfs(v, maxH, minH, cost + nextCost, a, b, s)
path.pop()
def solve(n, a, b, s, node):
global tree, bestCost, bestPath, path
tree = [None] * (n + 1)
bestCost = float('inf')
bestPath = []
path = []
for i in range(n):
id, value, left, right = node[i]
tree[id] = Node(value, left, right)
# 从根节点 1 开始搜索
dfs(1, tree[1].value, tree[1].value, 0, a, b, s)
return bestPath
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
idx = 0
n = data[idx]
idx += 1
a, b = data[idx], data[idx + 1]
idx += 2
s = data[idx]
idx += 1
node = []
for i in range(n):
node.append(data[idx:idx + 4])
idx += 4
ans = solve(n, a, b, s, node)
# 没有合法路线
if not ans:
print("")
return
# 输出最优路线
print(" ".join(map(str, ans)))
if __name__ == "__main__":
main()
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());
});
rl.on('close', () => {
const data = lines.join(' ').split(/\\s+/).map(Number);
let idx = 0;
const n = data[idx++];
const a = data[idx++];
const b = data[idx++];
const s = data[idx++];
const node = [];
for (let i = 0; i < n; i++) {
node.push(data.slice(idx, idx + 4));
idx += 4;
}
const ans = solve(n, a, b, s, node);
// 没有合法路线
if (ans.length === 0) {
console.log('');
return;
}
// 输出最优路线
console.log(ans.join(' '));
});
// 比较两条路线,选择字典序较小的路线
function smaller(a, b) {
const len = Math.min(a.length, b.length);
for (let i = 0; i < len; i++) {
if (a[i] !== b[i]) {
return a[i] < b[i];
}
}
return a.length < b.length;
}
let tree;
let bestCost;
let bestPath;
let path;
// DFS 搜索所有合法路线
function dfs(u, maxH, minH, cost, a, b, s) {
// 更新当前路径的最高和最低海拔
maxH = Math.max(maxH, tree[u].value);
minH = Math.min(minH, tree[u].value);
// 海拔极差超过阈值,直接剪枝
if (maxH – minH > s) {
return;
}
// 当前能耗已经超过最优答案,直接剪枝
if (cost > bestCost) {
return;
}
path.push(u);
// 到达叶子节点
if (tree[u].left === –1 && tree[u].right === –1) {
if (cost < bestCost ||
(cost === bestCost && (bestPath.length === 0 || smaller(path, bestPath)))) {
bestCost = cost;
bestPath = path.slice();
}
path.pop();
return;
}
// 计算下一条边的能耗
// 继续走向左子节点
if (tree[u].left !== –1) {
const v = tree[u].left;
const diff = tree[v].value – tree[u].value;
const nextCost = diff > 0 ? a * diff : b * (–diff);
dfs(v, maxH, minH, cost + nextCost, a, b, s);
}
// 继续走向右子节点
if (tree[u].right !== –1) {
const v = tree[u].right;
const diff = tree[v].value – tree[u].value;
const nextCost = diff > 0 ? a * diff : b * (–diff);
dfs(v, maxH, minH, cost + nextCost, a, b, s);
}
path.pop();
}
function solve(n, a, b, s, node) {
tree = new Array(n + 1);
bestCost = Infinity;
bestPath = [];
path = [];
for (let i = 0; i < n; i++) {
const [id, value, left, right] = node[i];
tree[id] = { value, left, right };
}
// 从根节点 1 开始搜索
dfs(1, tree[1].value, tree[1].value, 0, a, b, s);
return bestPath;
}
Go
package main
import (
"bufio"
"fmt"
"os"
)
type Node struct {
value int
left, right int
}
var tree []Node
var bestCost int64 = 1<<63 – 1
var bestPath []int
var path []int
// 比较两条路线,选择字典序较小的路线
func smaller(a, b []int) bool {
length := len(a)
if len(b) < length {
length = len(b)
}
for i := 0; i < length; i++ {
if a[i] != b[i] {
return a[i] < b[i]
}
}
return len(a) < len(b)
}
// DFS 搜索所有合法路线
func dfs(u, maxH, minH int, cost int64, a, b, s int) {
// 更新当前路径的最高和最低海拔
if tree[u].value > maxH {
maxH = tree[u].value
}
if tree[u].value < minH {
minH = tree[u].value
}
// 海拔极差超过阈值,直接剪枝
if maxH–minH > s {
return
}
// 当前能耗已经超过最优答案,直接剪枝
if cost > bestCost {
return
}
path = append(path, u)
// 到达叶子节点
if tree[u].left == –1 && tree[u].right == –1 {
if cost < bestCost ||
(cost == bestCost && (len(bestPath) == 0 || smaller(path, bestPath))) {
bestCost = cost
bestPath = append([]int(nil), path…)
}
path = path[:len(path)–1]
return
}
// 计算下一条边的能耗
// 继续走向左子节点
if tree[u].left != –1 {
v := tree[u].left
diff := tree[v].value – tree[u].value
var nextCost int64
if diff > 0 {
nextCost = int64(a) * int64(diff)
} else {
nextCost = int64(b) * int64(–diff)
}
dfs(v, maxH, minH, cost+nextCost, a, b, s)
}
// 继续走向右子节点
if tree[u].right != –1 {
v := tree[u].right
diff := tree[v].value – tree[u].value
var nextCost int64
if diff > 0 {
nextCost = int64(a) * int64(diff)
} else {
nextCost = int64(b) * int64(–diff)
}
dfs(v, maxH, minH, cost+nextCost, a, b, s)
}
path = path[:len(path)–1]
}
func solve(n, a, b, s int, node [][]int) []int {
tree = make([]Node, n+1)
bestCost = 1<<63 – 1
bestPath = []int{}
path = []int{}
for i := 0; i < n; i++ {
id := node[i][0]
value := node[i][1]
left := node[i][2]
right := node[i][3]
tree[id] = Node{value, left, right}
}
// 从根节点 1 开始搜索
dfs(1, tree[1].value, tree[1].value, 0, a, b, s)
return bestPath
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var n, a, b, s int
fmt.Fscan(in, &n)
fmt.Fscan(in, &a, &b)
fmt.Fscan(in, &s)
node := make([][]int, n)
for i := 0; i < n; i++ {
node[i] = make([]int, 4)
fmt.Fscan(in, &node[i][0], &node[i][1], &node[i][2], &node[i][3])
}
ans := solve(n, a, b, s, node)
// 没有合法路线
if len(ans) == 0 {
fmt.Fprintln(out)
return
}
// 输出最优路线
for i := 0; i < len(ans); i++ {
if i > 0 {
fmt.Fprint(out, " ")
}
fmt.Fprint(out, ans[i])
}
fmt.Fprintln(out)
}
C语言
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
typedef long long ll;
typedef struct {
int value;
int left, right;
} Node;
Node *tree;
int *bestPath, *path;
int bestLen = 0;
int pathLen = 0;
ll bestCost = LLONG_MAX;
// 比较两条路线,选择字典序较小的路线
int smaller(int a[], int lenA, int b[], int lenB) {
int len = lenA < lenB ? lenA : lenB;
for (int i = 0; i < len; i++) {
if (a[i] != b[i]) {
return a[i] < b[i];
}
}
return lenA < lenB;
}
// DFS 搜索所有合法路线
void dfs(int u, int maxH, int minH, ll cost, int a, int b, int s) {
// 更新当前路径的最高和最低海拔
if (tree[u].value > maxH) {
maxH = tree[u].value;
}
if (tree[u].value < minH) {
minH = tree[u].value;
}
// 海拔极差超过阈值,直接剪枝
if (maxH – minH > s) {
return;
}
// 当前能耗已经超过最优答案,直接剪枝
if (cost > bestCost) {
return;
}
path[pathLen++] = u;
// 到达叶子节点
if (tree[u].left == –1 && tree[u].right == –1) {
if (cost < bestCost ||
(cost == bestCost &&
(bestLen == 0 || smaller(path, pathLen, bestPath, bestLen)))) {
bestCost = cost;
bestLen = pathLen;
for (int i = 0; i < pathLen; i++) {
bestPath[i] = path[i];
}
}
pathLen—;
return;
}
// 计算下一条边的能耗
// 继续走向左子节点
if (tree[u].left != –1) {
int v = tree[u].left;
int diff = tree[v].value – tree[u].value;
ll nextCost;
if (diff > 0) {
nextCost = (ll)a * diff;
} else {
nextCost = (ll)b * (–diff);
}
dfs(v, maxH, minH, cost + nextCost, a, b, s);
}
// 继续走向右子节点
if (tree[u].right != –1) {
int v = tree[u].right;
int diff = tree[v].value – tree[u].value;
ll nextCost;
if (diff > 0) {
nextCost = (ll)a * diff;
} else {
nextCost = (ll)b * (–diff);
}
dfs(v, maxH, minH, cost + nextCost, a, b, s);
}
pathLen—;
}
int main() {
int n, a, b, s;
scanf("%d", &n);
scanf("%d %d", &a, &b);
scanf("%d", &s);
tree = (Node *)malloc((n + 1) * sizeof(Node));
bestPath = (int *)malloc((n + 1) * sizeof(int));
path = (int *)malloc((n + 1) * sizeof(int));
for (int i = 0; i < n; i++) {
int id, value, left, right;
scanf("%d %d %d %d", &id, &value, &left, &right);
tree[id].value = value;
tree[id].left = left;
tree[id].right = right;
}
// 从根节点 1 开始搜索
dfs(1, tree[1].value, tree[1].value, 0, a, b, s);
// 没有合法路线
if (bestLen == 0) {
printf("\\n");
} else {
// 输出最优路线
for (int i = 0; i < bestLen; i++) {
if (i > 0) {
printf(" ");
}
printf("%d", bestPath[i]);
}
printf("\\n");
}
free(tree);
free(bestPath);
free(path);
return 0;
}
网硕互联帮助中心实现100%通过率-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/11/20261106141640-6aede14864b2b-220x80.gif)


评论前必须登录!
注册