华为OD机试 - 猴子吃桃 - 二分查找(Java 2024 E卷 100分)

本文介绍了华为OD机试中的一道题目——猴子吃桃问题,利用二分查找法寻找孙悟空在天兵天将回来前吃完所有蟠桃的最小速度。详细阐述了解题思路,包括从回溯法到二分查找法的优化,并给出了Java算法源码示例。
摘要由CSDN通过智能技术生成

一、题目描述

孙悟空喜欢吃蟠桃,一天他乘守卫蟠桃园的天兵天将离开了而偷偷的来到王母娘娘的蟠桃园偷吃蟠桃。

已知蟠桃园有 N 棵蟠桃树,第 i 棵蟠桃树上有 N[i](大于 0)个蟠桃,天兵天将将在 H(不小于蟠桃树棵数)小时后回来。

孙悟空可以决定他吃蟠桃的速度 K(单位:个/小时),每个小时他会选择一颗蟠桃树,从中吃掉 K 个蟠桃,如果这棵树上的蟠桃数小于 K,他将吃掉这棵树上所有蟠桃,然后这一小时内不再吃其余蟠桃树上的蟠桃。

孙悟空喜欢慢慢吃,但仍想在天兵天将回来前将所有蟠桃吃完。

求孙悟空可以在 H 小时内吃掉所有蟠桃的最小速度 K(K 为整数)。

二、输入描述

从标准输入中读取一行数字,前面数字表示每棵数上蟠桃个数,最后的数字表示天兵天将将离开的时间。

三、输出描述

吃掉所有蟠桃的 最小速度 K(K 为整数)或 输入异常时输出 -1。

1、输入

3 11 6 7 8

2、输出

4

3、说明

天兵天将8个小时后回来,孙悟空吃掉所有蟠桃的最小速度4。

  1. 第1小时全部吃完第一棵树,吃3个,
  2. 第2小时吃4个,第二棵树剩7个,
  3. 第3小时吃4个,第二棵树剩3个,
  4. 第4小时吃3个,第二棵树吃完,
  5. 第5小时吃4个,第三棵树剩2个,
  6. 第6小时吃2个,第三棵树吃完,
  7. 第7小时吃4个,第4棵树剩3个,
  8. 第8小时吃3个,第4棵树吃完。

四、解题思路

题目描述有点复杂,多读几遍不难理解,意思就是:

一个小时只能在一棵桃树上吃,如果吃不完,下一个小时继续吃,如果吃完了,就不吃了,但不能去吃下一颗桃树,要守规矩。

  1. 输入几个数,表示每颗树的桃子数量;
  2. 在H个小时内,以最慢的速度将这些桃子全部吃完。

很明显的回溯问题,一个一个找呗,看哪个速度最合适。

五、Java算法源码

1、从速度1开始回溯,寻找H小时内吃完所有桃子的最小速度

假如孙猴子一小时吃100个桃子最合适,效率有点堪忧。

public class Test06 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] arr = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        // 天兵天将将离开的时间H
        int H = arr[arr.length - 1];

        // 获取每棵数上蟠桃个数
        arr = Arrays.copyOf(arr, arr.length - 1);

        // 从少到多排序
        Arrays.sort(arr);

        // 从速度1开始回溯,寻找H小时内吃完所有桃子的最小速度
        dfs(arr, H, 1);

        System.out.println(minSpeed);
        System.out.println("执行次数:" + num);
    }

    // 记录回溯次数,优化回溯算法
    static int num = 0;
    // 吃完所有桃子的最小速度
    static int minSpeed = 0;

    /**
     * 从速度1开始回溯,寻找H小时内吃完所有桃子的最小速度
     */
    private static void dfs(int[] arr, int H, int K) {
        int times = 0;
        for (int i = 0; i < arr.length; i++) {
            num++;
            // 以速度K吃完每颗桃树的时间
            times += arr[i] / K;
            if (arr[i] % K != 0) {
                times++;
            }
        }

        // 因为速度从小到大递增,当时间 <= H时,即最小速度
        if (times <= H) {
            minSpeed = K;
            return;
        } else {
            // 吃不完,加快速度
            dfs(arr, H, ++K);
        }
    }
}

输入:3 25 6 7 8
输出:7
执行次数:28

2、预测一个最可能的速度

所有桃子的数量除以总时间,得出每小时吃桃子数量,我觉得这个就是比较接近最小速度的最优解。

  1. 如果吃不完,就加快速度;
  2. 如果吃完了,就吃慢一点;
  3. 哈哈
public class Test08 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] arr = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        // 天兵天将将离开的时间H
        int H = arr[arr.length - 1];

        // 获取每棵数上蟠桃个数
        arr = Arrays.copyOf(arr, arr.length - 1);
        // 从少到多排序
        Arrays.sort(arr);

        // 预测最可能速度,优化回溯算法
        int sum = Arrays.stream(arr).sum();
        int possible = sum % H == 0 ? sum / H : sum / H + 1;

        System.out.println("预测最可能速度:" + possible);

        // 从最可能速度开始回溯,寻找H小时内吃完所有桃子的最小速度
        dfs(arr, H, possible);

        System.out.println(minSpeed);
        System.out.println("执行次数:" + num);
    }

    // 记录回溯次数,优化回溯算法
    static int num = 0;
    // 吃完所有桃子的最小速度
    static int minSpeed = 0;
    // 上一次吃完所有桃子的速度
    static int preSpeed = 0;

    private static void dfs(int[] arr, int H, int K) {
        int times = 0;
        for (int i = 0; i < arr.length; i++) {
            num++;
            // 以速度K吃完每颗桃树的时间
            times += arr[i] / K;
            if (arr[i] % K != 0) {
                times++;
            }
        }

        // 吃不完,加快速度
        if (times > H) {
            // 如果上次已经吃完,则上次吃完的速度即最小速度
            if (preSpeed != 0) {
                minSpeed = preSpeed;
                return;
            }
            dfs(arr, H, ++K);
        } else if (times < H) {// 吃完了,再吃慢一点
            preSpeed = K;
            dfs(arr, H, --K);
        } else {// 刚好吃完,返回最小速度
            minSpeed = K;
            return;
        }
    }
}

输入:3 25 6 7 8
输出:7
预测最可能速度:6
执行次数:12

3、一切都靠猜 + 回溯,有点太小儿科了,通过二分查找正统一下

public class Test09 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] arr = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        // 天兵天将将离开的时间H
        int H = arr[arr.length - 1];

        // 获取每棵数上蟠桃个数
        arr = Arrays.copyOf(arr, arr.length - 1);
        // 从少到多排序
        Arrays.sort(arr);

        int left = 1;
        int right = arr[arr.length - 1];
        while (left < right) {
            int mid = (left + right) / 2;
            // 吃完了,还能慢一点
            if (dfs(arr, H, mid)) {
                right = mid;
            } else {// 没吃完,吃快一点
                left = mid + 1;
            }
        }
        System.out.println(left);
        System.out.println("执行次数:" + num);
    }

    // 记录回溯次数,优化回溯算法
    static int num = 0;
    // 上一次吃完所有桃子的速度
    static int preSpeed = 0;

    private static boolean dfs(int[] arr, int H, int K) {
        int times = 0;
        for (int i = 0; i < arr.length; i++) {
            num++;
            // 以速度K吃完每颗桃树的时间
            times += arr[i] / K;
            if (arr[i] % K != 0) {
                times++;
            }
        }
        return times <= H;
    }
}

输入:3 25 6 7 8
输出:7
执行次数:16

通过对比,还是预测一个最可能的速度效率最高,嘿嘿~

六、效果展示

1、输入

3 25 6 7 8

2、输出

7

3、说明

在这里插入图片描述

华为OD机试-2023真主要考察了以下几个方面的知识点: 1. 数据结构与算法:目涉及了常见的数据结构和算法,如数组、链表、树、图、排序、搜索等。要求考生熟悉这些数据结构的基本操作和常用算法的实现方法。 2. 编程语言:目要求使用C++或Java语言完成编程任务,要求考生熟悉相应语言的语法和常用的库函数使用方法。 3. 网络通信:目涉及了网络通信相关的知识点,如TCP/IP协议、HTTP协议、socket编程等。要求考生了解网络通信的基本概念和实现方式。 4. 操作系统:目要求考生熟悉操作系统相关的知识,如进程管理、内存管理、文件系统等。要求考生了解操作系统的基本功能和实现原理。 5. 数据库:目涉及了数据库相关的知识点,如SQL语句的编写、数据库的设计和优化等。要求考生具备数据库的基本操作和设计能力。 6. 设计模式:目要求考生能够根据给定的需求设计相应的对象和类,并且符合设计模式的原则。要求考生熟悉常见的设计模式和其应用场景。 7. 系统设计与架构:目要求考生从整体上设计和实现一个系统,并考虑系统的性能、可扩展性等因素。要求考生了解系统设计的基本原则和方法。 以上是华为OD机试-2023真的一些考点类。考生在复习备考时,可以根据这些考点有针对性地进行学习和练习,提升自己的应试能力。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

哪 吒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值