华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试(JAVA)真题(2025A卷+E卷+B卷+C卷+D卷)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天优快云在线答疑。
一、题目描述
有一个荒岛,只有左右两个港口,只有一座桥连接这两个港口,现在有一群人需要从两个港口逃生,有的人往右逃生,有的往左逃生,如果两个人相遇,则PK,体力值大的能够打赢体力值小的,体力值相同则同归于尽,赢的人才能继续往前逃生,并较少相应地体力。
二、输入描述
一行非0整数,用空格隔开,正数代表向右逃生,负数代表向左逃生。
三、输出描述
最终能够逃生的人数。
四、解题思路
1、本题为什么采用栈?
栈是一个后进先出(LIFO, Last In First Out)的数据结构。这里使用栈来存储正数(向右逃生的人)。
当遇到负数(向左逃生的人)时,会检查栈顶的元素(也就是最后一个进入栈的向右逃生的人),并进行体力值的比较和PK处理。
栈非常适合这种场景,因为我们只需要考虑最新加入的向右逃生的人(栈顶元素),逐步处理每次遇到的负数(向左逃生的人)。
2、本题为什么采用贪心算法?
贪心算法是一种在每一步选择中都采取当前最优解的策略。在本题中,两个逃生的人相遇时,体力值大的赢,这就是贪心的核心思想。
每次 PK 的时候,只需要比较栈顶的正数和当前的负数,并根据体力值大小直接消耗体力或决定逃生人数。
通过贪心策略,每次都处理当前局部最优的情况,逐步得到最终能逃生的人数。
3、解题思路
题意是这样的:
体力值大的能够打赢体力值小的,体力值相同则同归于尽,赢的人才能继续往前逃生,并较少相应地体力。
正数代表向右逃生,负数代表向左逃生。
- 首先将输入的一行非零整数转换为一个整数数组,正数代表向右逃生,负数代表向左逃生。
- 遍历数组中的每一个元素:
- 如果遇到正数(向右逃生),将其压入栈中。
- 如果遇到负数(向左逃生),则通过栈顶元素(即向右逃生的人)的体力值与当前负数进行比较,模拟两者相遇后发生的 PK。
- PK 逻辑
- 如果栈顶的正数体力值大于负数:正数赢,栈顶的正数体力值减少,负数被完全消耗掉。
- 如果栈顶的正数体力值小于或等于负数:正数被弹出(栈顶元素被移除),负数的体力值减少或完全消耗。
- 如果栈为空(所有向右逃生的人都被消耗),负数将无法遇到任何人,可以直接存活。
- 计算最终结果
- 最后剩下的栈中元素表示仍然存活的向右逃生的人数。
- 遍历过程中累积的负数(向左逃生)如果没有被完全消耗,则表示能够逃生的向左逃生人数。
- 输出最终能够逃生的总人数,即剩余栈中的正数和累积存活的负数之和。
五、Java算法源码
1、我的原始代码:
有点小错误,没考虑负数在左,正数在右,无法相遇的情况,感谢@迷雾追踪同学的指正。
package com.guor.od;
import java.util.Scanner;
import java.util.*;
public class OdTest01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 一行非0整数,用空格隔开
int[] nums = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 正数
Stack<Integer> positiveStack = new Stack<Integer>();
// 负数
Stack<Integer> negativeStack = new Stack<Integer>();
for (int j = 0; j < nums.length; j++) {
int num = nums[j];
// 正数代表向右逃生
if (num > 0) {
positiveStack.push(num);
// 负数代表向左逃生
} else {
negativeStack.push(Math.abs(num));
}
// 看最后能抵消多少
while (!positiveStack.empty() && !negativeStack.empty()) {
// 体力值大的能够打赢体力值小的
if (positiveStack.peek() > negativeStack.peek()) {
positiveStack.push(positiveStack.pop() - negativeStack.pop());
} else if (positiveStack.peek() < negativeStack.peek()) {
negativeStack.push(negativeStack.pop() - positiveStack.pop());
} else {// 体力值相同则同归于尽
positiveStack.pop();
negativeStack.pop();
}
}
}
System.out.println(positiveStack.size() + negativeStack.size());
}
}
2、感谢@undefined提供的改进算法:
改进了如果负数在桥的左侧,正数在桥的右侧,没有机会相遇的情况。
package com.guor.od;
import java.util.*;
/**
* undefined提供算法
*/
public class OdTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 一行非0整数,用空格隔开
int[] nums = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 正数
Stack<Integer> positiveStack = new Stack<Integer>();
int ans = 0;
int leftMan = 0;
for (int j = 0; j < nums.length; j++) {
int num = nums[j];
// 正数代表向右逃生
if (num > 0) {
positiveStack.push(num);
// 负数代表向左逃生
} else {
leftMan = Math.abs(num);
// 看最后能抵消多少
while (!positiveStack.empty()) {
// 体力值大的能够打赢体力值小的
if (positiveStack.peek() > leftMan) {
positiveStack.push(positiveStack.pop() - leftMan);
leftMan = 0;
} else {
leftMan -= positiveStack.pop();
}
if(leftMan == 0) {
break;
}
}
//他已经抵消了所有向右相遇的人,他太累了,可以休息了
if(leftMan > 0){
ans++;
}
}
}
System.out.println(ans + positiveStack.size());
}
}
3、感谢@绿蚁新醅酒提供的改进算法,条条大道通罗马
package com.guor.od;
import java.util.*;
/**
* 绿蚁新醅酒 提供算法
*/
public class OdTest02 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 一行非0整数,用空格隔开
int[] nums = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 正数
Stack<Integer> positiveStack = new Stack<Integer>();
// 负数
Stack<Integer> negativeStack = new Stack<Integer>();
for (int j = 0; j < nums.length; j++) {
int num = nums[j];
// 正数,直接入栈,因为正数向右,与左边的元素都不碰撞
if (num > 0) {
positiveStack.push(num);
} else {
// 负数代表向左逃生,则需要判断是否已经有向右的,要不自己被消掉,要不消掉所有的向右的
int remain_num = Math.abs(num);
while (!positiveStack.empty()) {
int positive_top = positiveStack.peek();
if (remain_num > positive_top) {
// 消除当前的正数,还可以继续碰撞
remain_num -= positive_top;
positiveStack.pop();
} else if (remain_num < positive_top) {
// 消除当前的正数,然后修改top值
int modify_num = positive_top - num;
positiveStack.pop();
positiveStack.push(modify_num);
remain_num = 0;
break;
} else {
// 相等,同归于尽
positiveStack.pop();
remain_num = 0; // 被消除了
break;
}
}
// 如果还活着
if (remain_num > 0) {
negativeStack.push(-remain_num);
}
}
}
System.out.println(positiveStack.size() + negativeStack.size());
}
}
六、效果展示
1、输入
10 20 -20 -5 10
2、输出
2
3、说明
正数集合:10 20 10
负数集合: -20 -5
- 20和-20同归于尽;
- 10和-5,抵消5体力值;
- 最终能够逃生的人数2人。
🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2025 A卷 200分)
🏆本文收录于,华为OD机试(JAVA)真题(2025A卷+E卷+B卷+C卷+D卷)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天优快云在线答疑。