问题:
有N条绳子,它们的长度分别Li,i=1,⋯,N。如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长能有多长?(我们这里考虑每条绳子的长度都是整数,切最后切割出来的绳子的长度也是整数。如果遇到的问题绳子长度不是整数类型,可将绳子的长度先扩大10n 倍,转换成整数形式 )
解题思路:
这个题需要用二分查找法来解决,为了方便讲解,我们假设绳子有4条,长度分别是{802,743,457,539},需要切割出11条长度相同的绳子(这个数据来自PTA 切割绳子问题)
-
就我的理解,既然是用二分查找,那么就需要设置一个较为合适的区间[left right],将答案包括在这个区间里,那么我们就要先去把这样一个区间找出来。
//这下面的操作就是去寻找出这个区间 int left = 1;//理论上切割出绳子的最小值 int right = 10000;//理论上切割出绳子的最大值 for (int i = 0; i < n; i++) { ints[i] = scanner.nextInt();//读取每绳子的长度,并存入ints 数组内 if (ints[i] < right){ right = ints[i];//将最短的那条绳子的长度赋给 right } } //去调整left 和 right 的值,找到包含最优解的一个较为合适的区间 int res = 0;//res 表示切割出绳子的数量 for (int i = 0; i < n; i++) {//这个循环是去求当切割长度为right时,切割出来的绳子数量 res += ints[i] / right; } while (res>=k){//这个循环是为了使得当切割长度为right时,切割出来的绳子数量小于所需要的绳子数量 res = 0; int temp = right; right = right*2; left = temp; for (int i = 0; i < n; i++) { res += ints[i] / right; } }
-
在找到一个较为合适的区间[left right]后,就利用二分查找法去求出一个较优的解
//寻找一个较优值 int mid = 0;//区间的中间值 while (res != k){//二分查找经典做法,根据条件去缩小区间 res = 0; mid = (right-left)/2+left; for (int i = 0; i < n; i++) { res += ints[i] / mid; } if (res<k){ right = mid; }else if (res>k){ left=mid; } }
-
根据所需要的精度,让之前求出的较优值去逐渐靠近最优质的
while (true){//因为是求最大值,所以我们不断的去增加之前求出的较优值,直到这个值不再满足我们所需要的条件 mid++; int res01 = 0; for (int i = 0; i < n; i++) { res01 += ints[i] / mid; } if (res01 != k){ mid--; break; } }
完整代码
public static void main(String[] args) { //这部分操作是输入数据并将输入的 N 条绳子的长度 Len 存入一个整数数组里 Scanner scanner = new Scanner(System.in); String str = scanner.nextLine(); String[] strArr = str.split(" "); int n = Integer.parseInt(strArr[0]); int k = Integer.parseInt(strArr[1]); int[] ints = new int[n]; //设置左右区间,将最短的那根绳子设为右边界 int left = 1; int right = 10000; for (int i = 0; i < n; i++) { ints[i] = scanner.nextInt(); if (ints[i] < right){ right = ints[i]; } } scanner.close(); //改变left 和 right 的值,使最优解放在区间内 int res = 0; for (int i = 0; i < n; i++) { res += ints[i] / right; } while (res>=k){ res = 0; int temp = right; right = right*2; left = temp; for (int i = 0; i < n; i++) { res += ints[i] / right; } } //利用二分查找,找到一个较优的值 int mid = 0; while (res != k){ res = 0; mid = (right-left)/2+left; for (int i = 0; i < n; i++) { res += ints[i] / mid; } if (res<k){ right = mid; }else if (res>k){ left=mid; } } //因为求最大,所以逐渐将这一较优值增加,直到这一值不满足条件 while (true){ mid++; int res01 = 0; for (int i = 0; i < n; i++) { res01 += ints[i] / mid; } if (res01 != k){ mid--; break; } } System.out.println(mid); }
总结
我在写代码的时候是按照我之前的步骤来的,在写完代码之后,我感觉这个代码写的有些冗余了,之后需要继续完善。