算法:剪绳子

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

一、问题描述

二、常规解法

总结


提示:以下是本篇文章正文内容,下面案例可供参考

一、问题描述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。

请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

二、常规解法

解题思路:

这道题是有规律的,一个数可以有很多种不同的组成方法,但是恰恰是数字最接近的时候,乘积最大。

例如7:拆成两个数的组合有1+6  2+5  3+4  ,对应的乘积分别是6  10  12。拆成三个数的组合且不包含1的有 3+2+2,对应的乘积是12。最大的两种组合分别是3+4和3+2+2,这两种实际上没区别,因为4=2+2。

例如8:拆成两个数的组合有1+7  2+6  3+5  4+4  ,对应的乘积分别是7  12  15  16。拆成三个数的组合且不包含1的有 2+2+4  3+3+2,对应的乘积分别是16  18。最大的组合是3+3+2。

例如9:拆成两个数的组合有1+8  2+7  3+6  4+5  ,对应的乘积分别是8  14  18  20。拆成三个数的组合且不包含1的有 2+2+5  2+3+4  3+3+3,对应的乘积分别是20  24  27。最大的组合是3+3+3。拆成4个数的组合有2+2+2+3,对应的乘积是24。

综上来看,我们总结一下规律:

最大的组合,对应到每个子元素,发现他们之间的差值都是小于等于1,那怎么将一个值拆成差值最小的组合呢?我们用均等拆分来看看。

例如7:先拆出来一个元素1,剩余是6;如果我要再增加一个元素,并且增加完之后,所有元素值保持一致,那就是2个元素值为2的元素,剩余3;如果我按照上边的思路再分的时候,发现我前边两个元素都需要再加1,第三个元素要加3,一共需要4,剩余不满足,那么这种方式分配就只能到这里了。那么剩下的3怎么分配呢?我们来看看剩下的3够不够再分配一个元素2,发现可以,那么我们再分配出来一个2,当前组合为:2+2+2,剩余1,剩余1肯定不能单独再分配一个元素,否则跟没分配没什么区别,那么就将其加到第一个元素上,最终组合为:3+2+2

例如8:快速的看一下分配的过程

第一次分配   当前1 剩余7

第二次分配   当前2+2 剩余4

第三次分配   当前2+2+2 剩余2

第四次分配   当前3+3+2 剩余0

例如9:快速的看一下分配的过程

第一次分配   当前1 剩余8

第二次分配   当前2+2 剩余5

第三次分配   当前3+3+3 剩余0

代码示例: 

public void test() {
    int n = 11;
    // 记录拆分后的每个元素
    int[] array = new int[n];
    // 记录大于0的元素个数
    int size = 0;
    // 剩余数字
    int left = n;
    // 每个元素的值
    int value = 0;
    // 如果剩余的数字足够前边size个元素每个再+1, 并且下一个元素分配value+1的值, 那么继续
    while(left >= (size + 1) + (value + 1)) {
        // 元素位置向后移动
        size++;
        // 每个元素的值+1
        value++;
        // 对前size个元素重置赋值, 赋值为最新的value
        for (int i = 0; i < size; i++) {
            array[i] = value;
        }
        // 计算剩余的数字, 总数字减掉前size个元素的和
        left = n - size * value;
    }
    // 如果剩余数字不够再分配一轮, 且还有剩余, 进下边的分支
    if (left > 0) {
        // 如果剩下的数字够分配一个新的元素, 元素的值与已分配的一致, (特殊场景已分配的元素值不为2+剩余数字不为2), 优先分配一个元素
        if (left > value || (left == value && value != 2)) {
            array[size] = value;
            left = left - value;
        }
    }
    // 剩余数字不为0, 将其平摊到已分配的数字上, 每个数字+1, 直至分配完剩余数字
    if (left > 0) {
        for (int i = 0; i < left; i++) {
            array[i] = array[i] + 1;
        }
    }
    Long total = 1L;
    for (int i : array) {
        if (i == 0) {
            break;
        }
        // 输出大于0的元素
        System.out.println(i);
        // 计算乘积
        total = total * i;
    }
    System.out.println("乘积总和:" + total);
}

总结

详细注释已添加,快来练习吧,简单到有手就行!

你可能感兴趣的:(算法)