剑指Offer:[第24天 数学(中等)]--->剪绳子


一、题目描述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]*k[1]*…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

示例1:

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

示例2:

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

提示:

2 <= n <= 58


二、思路分析

注:思路分析中的一些内容和图片参考自力扣各位前辈的题解,感谢他们的无私奉献

方法一:动态规划

算法流程:
①我们想要求长度为 n n n 的绳子剪掉后的最大乘积,可以从前面比 n n n 小的绳子转移而来
②用一个 d p dp dp 数组记录从 0 0 0 n n n 长度的绳子剪掉后的最大乘积,也就是 d p [ i ] dp[i] dp[i] 表示长度为 i i i 的绳子剪成 m m m 段后的最大乘积,初始化 d p [ 0 ] = 0 dp[0] = 0 dp[0]=0(无意义), d p [ 1 ] = 0 dp[1] = 0 dp[1]=0(无意义), d p [ 2 ] = 1 dp[2] = 1 dp[2]=1
③现在有三种情况:
----只剪一刀:我们先把绳子剪掉第一段(长度为 j j j),长度乘积即为 j × ( i − j ) j \times (i - j) j×(ij)
----剪两刀及以上:剪了第一段后,剩下 i − j i - j ij 长度继续剪,长度乘积即为 j × d p [ i − j ] j \times dp[i - j] j×dp[ij]
—不剪:长度为 d p [ i ] dp[i] dp[i]
注意:如果只剪掉长度为 1 1 1,对最后的乘积无任何增益,所以要剪的话是从长度为2开始剪。
④最终更新 d p [ i ] dp[i] dp[i],就是从上面三种方案中选最大值,转移方程如下
d p [ i ] = m a x ( d p [ i ] , m a x ( j × ( i − j ) , j × d p [ i − j ] ) ) dp[i] = max(dp[i], max(j \times (i - j), j \times dp[i - j])) dp[i]=max(dp[i],max(j×(ij),j×dp[ij]))
⑥最后返回 d p [ n ] dp[n] dp[n]即可
复杂度分析:
时间复杂度 O ( N 2 ) \rm{O(N ^ 2)} O(N2):每次两层循环,最坏情况下循环 N 2 \rm{N^2} N2
空间复杂度 O ( N ) \rm{O(N)} O(N):用于保存动态方程各项的数组占用O(N)空间


方法二:贪心算法

贪心思路:
设一绳子长度为 n ( n > 1 ) n(n>1) n(n>1),则其必可被切分为两段 n = n 1 + n 2 n=n_1+n_2 n=n1+n2。根据经验推测,切分的两数字乘积往往比原数字更大,即往往有 n 1 × n 2 > n 1 + n 2 = n n_1 \times n_2 > n_1 + n_2 = n n1×n2>n1+n2=n
例如绳子长度为 6 6 6 6 = 3 + 3 < 3 × 3 = 9 6 = 3 + 3 < 3 \times 3 = 9 6=3+3<3×3=9。也有少数反例,例如 2 2 2 2 = 1 + 1 > 1 × 1 = 1 2 = 1 + 1 > 1 \times 1 = 1 2=1+1>1×1=1
推论:
①合理的切分方案可以带来更大的乘积
设一绳子长度为 n ( n > 1 ) n ( n>1) n(n>1),切分为两段 n = n 1 + n 2 n=n_1+n_2 n=n1+n2,切分为三段 n = n 1 + n 2 + n 3 n=n_1+n_2+n_3 n=n1+n2+n3。根据经验推测,三段的乘积往往更大,即往往有 n 1 n 2 n 3 > n 1 n 2 n_1 n_2 n_3 > n_1 n_2 n1n2n3>n1n2。例如绳子长度为 9 9 9:两段 9 = 4 + 5 9=4+5 9=4+5 和三段 9 = 3 + 3 + 3 9=3+3+3 9=3+3+3,则有 4 × 5 < 3 × 3 × 3 4 \times 5 < 3 \times 3 \times 3 4×5<3×3×3。也有少数反例,例如 6 6 6:两段 6 = 3 + 3 6=3+3 6=3+3 和三段 6 = 2 + 2 + 2 6=2+2+2 6=2+2+2,则有 3 × 3 > 2 × 2 × 2 3 \times 3 > 2 \times 2 \times 2 3×3>2×2×2
②若切分方案合理,绳子段切分的越多,乘积越大。
总体上看,貌似长绳子切分为越多段乘积越大,但其实到某个长度分界点后,乘积到达最大值,就不应再切分了。问题转化: 是否有优先级最高的长度 x x x 存在?若有,则应该尽可能把绳子以 x x x 长度切为多段,以获取最大乘积。
③为使乘积最大,只有长度为 2 2 2 3 3 3 的绳子不应再切分,且 3 3 3 2 2 2 更优(详情见下表)
在这里插入图片描述
切分规则:
最优: 3 3 3。把绳子尽可能切为多个长度为 3 3 3 的片段,留下的最后一段绳子的长度可能为 0 , 1 , 2 0,1,2 0,1,2 三种情况
次优: 2 2 2。若最后一段绳子长度为 2 2 2,则保留,不再拆为 1 + 1 1+1 1+1
最差: 1 1 1。若最后一段绳子长度为 1 1 1,则应把一份 3 + 1 3 +1 3+1 替换为 2 + 2 2 + 2 2+2,因为 2 × 2 > 3 × 1 2 \times 2 > 3 \times 1 2×2>3×1
算法流程:
①当 n ≤ 3 n \leq 3 n3 时,按照规则应不切分,但由于题目要求必须剪成 m > 1 m>1 m>1 段,因此必须剪出一段长度为 1 1 1 的绳子,即返回 n − 1 n - 1 n1
②当 n > 3 n>3 n>3 时,求 n n n 除以 3 3 3 的整数部分 a a a 和余数部分 b b b(即 n = 3 a + b n = 3a + b n=3a+b),并分为以下三种情况:
----当 b = 0 b = 0 b=0 时,直接返回 3 a 3^a 3a
----当 b = 1 b = 1 b=1 时,要将一个 1 + 3 1 + 3 1+3 转换为 2 + 2 2+2 2+2,因此返回 3 a − 1 × 4 3^{a-1} \times 4 3a1×4
----当 b = 2 b = 2 b=2 时,返回 3 a × 2 3^a \times 2 3a×2
在这里插入图片描述
复杂度分析:
时间复杂度 O ( 1 ) \rm{O(1)} O(1):仅有求整、求余、次方运算。
空间复杂度 O ( 1 ) \rm{O(1)} O(1):变量ab使用常数大小额外空间


三、整体代码

动态规划整体代码如下

int max(int a, int b){
    return a > b ? a : b;
}

int cuttingRope(int n){
    int* dp = (int*)malloc(sizeof(int)*(n+1));
    dp[0] = 0;
    dp[1] = 0;
    dp[2] = 1;
    for(int i = 3; i <= n; i++){
        for(int j = 2; j < i; j++){
            dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]));
        }
    }

    return dp[n];
}

运行,测试通过
在这里插入图片描述


贪心算法整体代码如下

int cuttingRope(int n){
    if(n <= 3) return n-1;
    int a = n / 3, b = n % 3;
    if(b == 0) return (int)pow(3, a); //3的整倍数,直接返回3^a
    if(b == 1) return (int)pow(3, a-1)*4; //多了1,取一节3剪成2×2,返回3^(a-1)*4
    return (int)pow(3, a) * 2;        //多了2,直接返回3^a*2
}

运行,测试通过
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知初与修一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值