木头切割问题
给定长度为n的数组,每个元素代表一个木头的长度,木头可以任意截断,从这堆木头中截出至少k个相同长度为m的木块。已知k,求max(m)。
输入两行,第一行n, k,第二行为数组序列。输出最大值。
输入 5 5 4 7 2 10 5 输出 4 解释:如图,最多可以把它分成5段长度为4的木头
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();
}
}