剪绳子问题

时间:2022-07-22
本文章向大家介绍剪绳子问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一道经典的贪心+二分问题。

问题描述:

一开始有一长度为s的绳子,每次从现有的绳子中选择以大小大于1的绳子假设其大小为k,将其剪为a 和 k - a(其中 1 <= a < k)两段,得到收益为a * (k - a)。

给定s, m,求最少剪几次可以得到至少m的收益,无法达到输出-1。

2 <= s <=1e3 1 <= m <= 1e9
Input : s = 333 m = 43434
output : 4

解决方案

首先我们必须知道该结论对于长度为s的绳子切n段最大收益的切法为每次切s的n分之一。即第一次切当前长度的1/n,第二次切当前长度的1/(n - 1) ……

由于随着切割次数的增加其切割的最大收益总是增加的,因此我们可以先枚举出可行解的上下界(下界为1,上界为s - 1),然后对上下界进行二分,得到中间值后使用上述贪心策略进行切割,然后判断当前切割次数能否达到m。如此找到满足条件的最小的切割次数即可。

代码如下:

    public int selution(int s, int m) {
        int left = 1;
        int right = s - 1;
        if(!canSatisfy(s, m, right)) {
            return -1;
        }
        while(left < right) {
            int mid = left + (right - left) / 2;
            if(canSatisfy(s, m, mid)) {
                right = mid;
            }else {
                left = mid + 1;
            }
            System.out.println(left + " " + right);
        }
        return left;
    }
    /**
     * @param num 为切割的次数
     * @return
     */
    public boolean canSatisfy(int s, int m, int num) {
        // 切num次会切num + 1段
        int temp = s / (num + 1);
        for(int i = 0; i < num; i++) {
            s -= temp;
            m -= s * temp;
            // 每次切割当前长度的 1 / num - i
            temp = s / (num - i);
            if(m <= 0) {
                return true;
            }
        }
        return false;
    }