【剑指offer】79.剪绳子

总目录:

算法之旅导航目录

 

1.问题描述

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

数据范围: 2≤n≤60
进阶:空间复杂度 O(1),时间复杂度 O(n)

 

2.问题分析

数学分析、贪心算法

可以证明如果把整数n分为两部分,那么这两部分的值相差越小乘积越大,因此尽量将绳子等分将获得最大乘积,问题是几等分

如果我们把长度为n的绳子分为x段,则每段只有在长度相等的时候乘积最大,那么每段的长度是n/x。所以他们的乘积是(n/x)^x。求该函数的极值:

 

 

 通过对函数求导我们发现,当x=n/e的时候,也就是每段绳子的长度是n/x=n/(n/e)=e的时候乘积最大。在e=2.71....的两侧,3相对于2更靠近e,因此长度为3一段更理想。

后续使用贪心思想,不断将绳子分成每段长度为3即可,不足3的可以考虑,如果最后剩余的是2,直接乘上,如果最后剩余的是1,则取出一个3组成4分成长度为2的两段,因为1*3<2*2

 

动态规划

定义一个数组dp,其中dp[i]表示的是长度为i的绳子能得到的最大乘积。我们先把长度为i的绳子拆成两部分,一部分是j,另一部分是i-j,那么会有下面4种情况

(1)j和i-j都不能再拆了,dp[i]=j*(i-j);

(2)j能拆,i-j不能拆,dp[i]=dp[j]*(i-j);

(3)j不能拆,i-j能拆,dp[i]=j*dp[i-j];

(4)j和i-j都能拆,dp[i]=dp[j]*dp[i-j];

递推公式如下

dp[i] = max(dp[i], (max(j, dp[j])) * (max(i - j, dp[i - j])));其中j的取值为[1,i]

问题是初始值如何判定:

i=1时,不能拆,dp[1]=1

i>=2时,套用公式即可获得。


3.代码实例

数学分析、贪心算法

 1 class Solution {
 2   public:
 3     int cutRope(int number) {
 4         if (number == 2 || number == 3)
 5             return number - 1;
 6         int res = 1;
 7         while (number > 4) {
 8             //如果target大于4,我们不停的让他减去3
 9             number = number - 3;
10             //计算每段的乘积
11             res = res * 3;
12         }
13         return number * res;
14     }
15 };

 

 

动态规划

 1 class Solution {
 2   public:
 3     int cutRope(int number) {
 4         vector<int> dp(number + 1, 0);
 5         dp[1] = 1;
 6         for (int i = 2; i <= number; i++) {
 7             for (int j = 1; j <= i; j++) {
 8                 //这里面包含了拆或者不拆、拆多长的抉择
 9                 dp[i] = max(dp[i], (max(j, dp[j])) * (max(i - j, dp[i - j])));
10             }
11         }
12 
13         return dp[number];
14     }
15 };

 

posted @ 2022-12-07 19:14  啊原来是这样呀  阅读(29)  评论(0编辑  收藏  举报