算法面试题:切割木头,给定一个数组nums,nums[i]表示每个木头的长度,木头可以截断,现在需要k根长度一样的木头,每根木头最大长度为多少

今天在网上看到一道这样的算法面试题,第一眼就不小心看到了答案的关键字眼“二分”。
乍一看有点不理解,二分一般是在有序的情况下使用,这个数组并非有序,如何二分?
在这里插入图片描述
于是有空的时候,重新去网上找到了一个类似的oj题,题目如下,地址我贴在这里,感兴趣的朋友可以去尝试一下。 https://www.luogu.com.cn/problem/P2440
在这里插入图片描述
首先采用了最简单的方法,即两重循环暴力解决问题,时间复杂度大约是n²,果不其然有一个测试数据超时了

#include<iostream>
#include<algorithm>
using namespace std;
int main() {
    
    
	int n, k;
	cin >> n >> k;
	int nums[n + 10];
	int min = 1, maxx = 0;
	for(int i = 0; i < n; i++) {
    
    
		cin >> nums[i];
		maxx = max(maxx, nums[i]); // 找出最大值
	}
	int res = 0;
	// 从1cm开始截取一直到最大值
	for(int i = 1; i < maxx; i++) {
    
    
		int cnt = 0;
		for(int j = 0; j < n; j++) {
    
    
			cnt += nums[j] / i;
			if(cnt >= k) {
    
    
				res = i;
				break;
			}
		}
		// 如果截出的数量不足k,则跳出
		if(cnt < k) {
    
    
			break;
		}
	}
	cout << res << endl;
}

然后我想起了那个关键字眼“二分”,于是思考了一下,采用了二分的方法。
这里所谓的二分,不是二分数组,而是二分能截取的最小值min1厘米和木头最大长度max。最小值到最大值之间肯定是有序的。

首先求出min和max的中间值mid,假设截取每根木头长度为mid,能截取到k根或者超过k根,则说明mid是符合题意的,这时候需要min设置为mid+1,来检查后半段是否有更大的长度,能截取到k根。

当然,如果一开始截取的时候不足k根,那就要将最大值左移,max设置为mid-1,检查前半段是否有满足题意的k。

以下是二分法的C++代码和java代码
C++代码:

#include<iostream>
#include<algorithm>
using namespace std;
int main() {
    
    
	int n, k;
	cin >> n >> k;
	int nums[n + 10];
	int min = 1, maxx = 0;
	for(int i = 0; i < n; i++) {
    
    
		cin >> nums[i];
		maxx = max(maxx, nums[i]);
	}
	int res = 0;
	// 二分
	while(min <= maxx) {
    
    
		int cnt = 0;
		int mid = (min + maxx) / 2;
		for(int i = 0; i < n; i++) {
    
    
			cnt += nums[i] / mid;
		}
		// 大于等于k根木头,则继续截
		if(cnt >= k) {
    
    
			min = mid + 1;
			res = mid;
		} else if(cnt < k) {
    
    
		// 不足则在[min, mid - 1]继续截
			maxx = mid - 1;
		}
	}
	cout << res << endl;
}

java代码:

import java.util.Scanner;

public class Main{
    
    
	public static void main(String[] args) {
    
    
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int k = sc.nextInt();
		int min = 1, max = 0; 
		int[] nums = new int[n];
		for (int i = 0; i < n; i++) {
    
    
			nums[i] = sc.nextInt();
			max = Math.max(max, nums[i]);
		}
		int res = 0;
		while (min <= max) {
    
    
			int cnt = 0;
			int mid = (min + max) / 2;
			for (int i = 0; i < n; i++) {
    
    
				cnt += nums[i] / mid;
			}
			if (cnt >= k) {
    
    
				min = mid + 1;
				res = mid;
			} else if (cnt < k) {
    
    
				max = mid - 1;
			}
		}
		System.out.println(res);
	}
}

本人小白一枚,如果大佬们有更好的方法,希望可以为我指点迷津。

猜你喜欢

转载自blog.csdn.net/weixin_51993595/article/details/120499737