华为OD机试 - 荒岛求生 - 栈Stack、贪心算法(Java 2025 B卷 100分)

该博客详细介绍了华为OD机试中的一道题目——荒岛求生,涉及栈的应用。题目要求根据一组整数判断能成功逃生的人数,正数代表向右逃生,负数向左。通过比较正负数的绝对值决定胜负,相同则同归于尽。博主分享了原始代码及两位读者提供的改进算法,最后展示了输入输出示例并进行了解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

华为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、解题思路

题意是这样的:

体力值大的能够打赢体力值小的,体力值相同则同归于尽,赢的人才能继续往前逃生,并较少相应地体力。
正数代表向右逃生,负数代表向左逃生。

  1. 首先将输入的一行非零整数转换为一个整数数组,正数代表向右逃生,负数代表向左逃生。
  2. 遍历数组中的每一个元素:
    • 如果遇到正数(向右逃生),将其压入栈中。
    • 如果遇到负数(向左逃生),则通过栈顶元素(即向右逃生的人)的体力值与当前负数进行比较,模拟两者相遇后发生的 PK。
  3. PK 逻辑
    • 如果栈顶的正数体力值大于负数:正数赢,栈顶的正数体力值减少,负数被完全消耗掉。
    • 如果栈顶的正数体力值小于或等于负数:正数被弹出(栈顶元素被移除),负数的体力值减少或完全消耗。
    • 如果栈为空(所有向右逃生的人都被消耗),负数将无法遇到任何人,可以直接存活。
  4. 计算最终结果
    • 最后剩下的栈中元素表示仍然存活的向右逃生的人数。
    • 遍历过程中累积的负数(向左逃生)如果没有被完全消耗,则表示能够逃生的向左逃生人数。
  5. 输出最终能够逃生的总人数,即剩余栈中的正数和累积存活的负数之和。

五、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算法的适用场景,发现新题目,随时更新,全天优快云在线答疑。

在这里插入图片描述

荒岛逃生游戏》是一个由Java语言开发游戏。在这个游戏中,玩家将扮演一个被困在荒岛上的角色,需要通过解谜和探索来找到逃生的方法。 在游戏开始时,玩家将被遗弃在一个荒凉、无人的岛屿上。这个岛屿上有各种各样的谜题和障碍,玩家需要通过收集资源和寻找线索来解决这些谜题。玩家可以使用一些基本的工具和装备,如刀子、火柴和绳子,来帮助他们在岛上生存和逃生游戏中的角色会面临各种挑战,如寻找食物和水源、制作简单工具以及躲避危险的动物等。玩家需要运用他们的观察力、推理能力和创造力来克服这些挑战。 随着玩家逐渐解开谜题,他们将发现岛上隐藏着一些秘密和宝藏。这些宝藏可以提供额外的资源和帮助玩家更快地逃离岛屿。但是,玩家需要小心,因为任何错误的决策都可能导致失败和游戏结束。 《荒岛逃生游戏》通过提供一个真实、紧张和刺激的荒岛求生体验,让玩家在逃生的过程中体验到不同的挑战和乐趣。这款游戏还具有一定的教育意义,可以帮助玩家培养问题解决和团队合作的能力。 总之,《荒岛逃生游戏》是一款极具挑战性和娱乐性的游戏,通过使用Java语言开发,为玩家带来了一个逃生的冒险和刺激的体验。这款游戏不仅能满足玩家对游戏的娱乐需求,还能锻炼玩家的思考和决策能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值