2.4.蓝桥杯-分巧克力

问题描述

儿童节那天有 K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 N块巧克力,其中第 i块是 Hi×Wi 的方格组成的长方形。为了公平起见,

小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足:

  1. 形状是正方形,边长是整数;

  2. 大小相同;

例如一块 6×5的巧克力可以切出 6 块 2×2的巧克力或者 2 块 3×3 的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

输入描述

第一行包含两个整数 N,K (1≤N,K≤10⁵)。

以下 N 行每行包含两个整数 Hi,Wi(1≤Hi,Wi≤10⁵)。

输入保证每位小朋友至少能获得一块 1x1 的巧克力。

输出描述

输出切出的正方形巧克力最大可能的边长。

输入输出样例

示例

输入

复制代码
2 10
6 5
5 6

输出

复制代码
2

运行限制

  • 最大运行时间:2s
  • 最大运行内存: 256M

参考代码:

java 复制代码
package Practice3;

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        /*1.输入巧克力个数和人数*/
        int N = scan.nextInt(); //巧克力个数
        int K = scan.nextInt(); //人数
        /*2.定义二维数组记录各个巧克力的长、宽->列代表各个巧克力个数,行代表巧克力长、宽*/
        int[][] chocolate = new int[N][2];
        /*3.输入巧克力长、宽*/
        for (int i = 0; i < N; i++) {
            //输入长添加到巧克力数组中
            chocolate[i][0] = scan.nextInt();
            //输入宽添加到巧克力数组中
            chocolate[i][1] = scan.nextInt();
        }
        /*核心思路:由于要保证每个人都分到巧克力,那么巧克力的个数必须尽可能的多,每一块巧克力必须是正方形,
         * 因此每一块巧克力的边越短越好,但还有一个要求是巧克力需要尽可能地大,
         * 那么就需要试一试比当前边长还大的边长是否也符合要求,如果符合,就需要取较大的边
         * ->巧克力边长必须是整数,那么边长最小为1,
         * 巧克力边长最大为100000(此时是1块巧克力,1个人,巧克力长、宽最大都是100000)
         * 显然,从1开始找边长的话效率很低,因此可以采用二分法进行判断
         * ->如果当前处理的边得出最终可分得的人数小于目标人数,就说明取的边长较大,就需要用小的边长重新计算,反之用大的边长*/
        /*4.定义变量记录最大边长*/
        int result;
        /*5.调用maxLen函数得出最大边长*/
        result = maxLen(chocolate, K);
        /*6.输出*/
        System.out.println(result);
    }

    //定义方法用来得出巧克力最大可切成的边长
    private static int maxLen(int[][] chocolate, int K) { //K是人数
        //这里不需要索引,就是找边长
        /*1.定义变量记录边长边界值、中间值*/
        int low = 1;
        int high = 100000;
        int mid;
        /*2.定义变量记录最终最大边长*/
        int max = 0; //max作为最终返回值需要初始化,如果不初始化的话最终可能返回的是空值
        /*3.定义变量记录当前分给多少人巧克力->人数可能会很大,用long防止溢出(long范围大,可隐式转换)*/
        long number = 0;
        /*4.开始查找,需要循环,循环条件low<=high(代表只要搜索区间不为空,就继续搜索,直到找到所有可能边长中的最大值),
        如果循环条件为number < K && flag == false,这会导致在某些情况下提前终止搜索,
         flag==false表示flag为false时才可能进行循环,如果flag为true了,表示找到了一个可行解,
         但之后是否还有更优的可行解就无法继续查找了,因为flag为true就不会再循环了->可能会漏解*/
        while (low<=high) {
            /*5.每一次循环都是重新分,那么分给的人数需要清0*/
            number = 0;
            /*6.取中间边长*/
            //mid = (low + high) / 2;也对,但是当low和high都很大时可能溢出
            mid = low + (high - low) / 2; //这里有做差,不容易溢出,而且等价mid = (low + high) / 2;
            /*7.得出分给的人数*/
            for (int i = 0; i < chocolate.length; i++) {
                number = number + (chocolate[i][0] / mid) * (chocolate[i][1] / mid);
            }
            /*8.判断*/
            if (number < K) {
                //意味着分的大了,取左范围->用小的边长
                high = mid - 1;
            } else if (number >= K) {
                //意味着每个人都能分到巧克力了,先记录当前正确的边长
                max = mid;
                //再尝试更大的边长
                low = mid + 1;
            }
        }
        /*9.返回最大边长*/
        return max;
    }
}

上述代码可优化,如下:

java 复制代码
package Practice3;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int N = scan.nextInt();
        int K = scan.nextInt();

        int[][] chocolate = new int[N][2];
        int maxSide = 0;  // 记录最大可能边长(二分上界)

        for (int i = 0; i < N; i++) {
            chocolate[i][0] = scan.nextInt();
            chocolate[i][1] = scan.nextInt();
            // 更新最大可能边长(巧克力能切出的最大正方形边长受限于最小边)
            maxSide = Math.max(maxSide, Math.min(chocolate[i][0], chocolate[i][1]));
        }

        int result = maxLen(chocolate, K, maxSide); //result是最终得出的边长
        System.out.println(result);
    }

    private static int maxLen(int[][] chocolate, int K, int maxPossible) {
        int low = 1;
        int high = maxPossible > 0 ? maxPossible : 100000; //high也是二分上界
        int max = 0;

        while (low <= high) {
            int mid = low + (high - low) / 2;  // 防止溢出
            long number = 0;

            // 计算当前边长能切出多少块
            for (int i = 0; i < chocolate.length; i++) {
                number += (long) (chocolate[i][0] / mid) * (chocolate[i][1] / mid);
                // 提前结束,优化性能
                if (number >= K) {
                    break;
                }
            }

            if (number >= K) {
                // 当前边长可行,记录并尝试更大的
                max = mid;
                low = mid + 1;
            } else {
                // 当前边长不可行,尝试更小的
                high = mid - 1;
            }
        }

        return max;
    }
}
相关推荐
江畔柳前堤8 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore9 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考10 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX11 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面12 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号12 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽12 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI12 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者14 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者14 小时前
【征程6】校准量化中HistogramObserver解析
算法