Skip to content

木头切割问题

约 571 字大约 2 分钟

最大化最小值问题二分查找

2025-03-01

给定长度为n的数组,每个元素代表一个木头的长度,木头可以任意截断,从这堆木头中截出至少k个相同长度为m的木块。已知k,求max(m)。

输入两行,第一行n, k,第二行为数组序列。输出最大值。

输入 5 5 4 7 2 10 5 输出 4 解释:如图,最多可以把它分成5段长度为4的木头

img.png

ps:数据保证有解,即结果至少是1。

最大化最小值问题,二分查找左边界

初始化:设定搜索范围 [1, max(array)]max(array) 是数组中最长的木头的长度。

二分查找

  • 计算中间值 mid
  • 计算能够截取的长度为 mid 的木块总数。
  • 如果能够截取的木块数量大于等于 k,则表示 mid 是可能的一个解,继续尝试更大的值(更新左边界)。
  • 如果能够截取的木块数量小于 k,则表示 mid 太大,需要尝试更小的值(更新右边界)。

最终解:当搜索范围收敛时,左边界即为最大可行的 m

m越大,k越小,它俩是反比关系,所以要求大于等于k时最大的m。

得寸进尺。

import java.util.Scanner;

public class Solution {

    // 检查长度为 mid 的木块能否截出至少 k 个
    public static boolean canCut(int[] arr, int mid, int k) {
        int count = 0;
        for (int length : arr) {
            count += length / mid; // 计算每个木头可以截出的长度为 mid 的木块数量
        }
        return count >= k;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int k = scanner.nextInt();
        int[] arr = new int[n];
        
        // 读取数组元素
        for (int i = 0; i < n; i++) {
            arr[i] = scanner.nextInt();
        }

        // 初始化二分查找的左右边界
        int left = 1;
        int right = 0;
        for (int length : arr) {
            // 重点
            right = Math.max(right, length);
        }

        // 二分查找上界
        while (left < right) { // 左开右闭
            int mid = (left + right + 1) / 2; // 计算上位中间值
            if (canCut(arr, mid, k)) {
                left = mid; // 如果能截出至少 k 个长度为 mid 的木块,尝试更大的长度
            } else {
                right = mid - 1; // 否则尝试更小的长度
            }
        }

        // 输出结果
        System.out.println(left);
        scanner.close();
    }
}