切割绳子问题(附 java 代码)

问题:

N条绳子,它们的长度分别Li,i=1,⋯,N。如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长能有多长?(我们这里考虑每条绳子的长度都是整数,切最后切割出来的绳子的长度也是整数。如果遇到的问题绳子长度不是整数类型,可将绳子的长度先扩大10n 倍,转换成整数形式 )

解题思路:

这个题需要用二分查找法来解决,为了方便讲解,我们假设绳子有4条,长度分别是{802,743,457,539},需要切割出11条长度相同的绳子(这个数据来自PTA 切割绳子问题

  1. 就我的理解,既然是用二分查找,那么就需要设置一个较为合适的区间[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;
    }
}
  1. 在找到一个较为合适的区间[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;
        }
    }
  2. 根据所需要的精度,让之前求出的较优值去逐渐靠近最优质的

    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);
    }

总结

我在写代码的时候是按照我之前的步骤来的,在写完代码之后,我感觉这个代码写的有些冗余了,之后需要继续完善。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值