剑指offer14-I剪绳子

题目描述

给你一根长度为 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。

输入描述

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

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:2 <= n <= 58

思路描述

思路1 贪心算法

题目中 n = a 1 + a 2 + . . . + a m n = a_1+a_2+...+a_m n=a1+a2+...+am,求 a 1 ∗ a 2 ∗ . . . ∗ a m a_1*a_2*...*a_m a1a2...am的最大值。根据算术平均不等式可知:
a 1 + a 2 + . . . + a m m ≥ a 1 ∗ a 2 ∗ . . . ∗ a m m {a_1+a_2+...+a_m \over m} \geq \sqrt [m]{a_1*a_2*...*a_m} ma1+a2+...+amma1a2...am
当且仅当 a 1 = a 2 = a 3 = . . . = a m = n m a_1 = a_2 = a_3 = ... = a_m = {n \over m} a1=a2=a3=...=am=mn,假设 n = a x n = ax n=ax,那么乘积为 x a x^a xa,即将n等分成a个x段的绳子的乘积是 x a x^a xa
x a = x n x = ( x 1 x ) n x^a = x^{\frac {n}{x}} = (x^{\frac {1}{x}})^n xa=xxn=(xx1)n已知n为常数,那么,令 y = x 1 x y = x^{\frac {1}{x}} y=xx1取使得y最大的x值,对y进行求导如下:
l n y = 1 x l n x , 两 边 同 时 取 l n lny = {1 \over x}lnx,两边同时取ln lny=x1lnx,ln
1 y y ′ = 1 − l n x x 2 , 两 边 同 时 求 导 数 {1 \over y}y' = {1-lnx \over x^2},两边同时求导数 y1y=x21lnx,
y ′ = 1 − l n x x 2 x 1 x , 当 x = e 时 , y ′ = 0 y' = {1-lnx \over x^2} x^{\frac {1}{x}},当x = e时,y' = 0 y=x21lnxxx1x=ey=0
易知ey的极大值点, e ≈ 2.71328 e \approx 2.71328 e2.71328,因此将n分成由2和3组成的数最好,是2还是3,将2和3代入公式可知 2 1 2 < 3 1 3 2^{1 \over 2} < 3^{1 \over 3} 221<331,因此能分成3的尽量都分成3,但是有一个特例因为1是极差的选择,因此,对于长度为4的绳子应分成22而不是13,因此下面就分成三种情况进行讨论, n = 3 a + b n = 3^a+b n=3a+b, n = 2 n=2 n=2 n = 3 n=3 n=3单独讨论,因此题目要求必须至少分成2段, n = 2 n=2 n=2时,返回1, n = 3 n=3 n=3时,返回2,其他情况按照 n = 3 a + b n = 3^a+b n=3a+b讨论:

  • 当b=0时,返回pow(3,a);
  • 当b=1时,返回pow(3,a-1)*4,要拿出一个3和1组成2*2;
  • 当b=2时,返回pow(3,a)*2。
class Solution {
public:
    int cuttingRope(int n) {
       if(n==2) return 1;
       if(n==3) return 2;
       int left = n%3;
       int times = n/3;
       if(left==0) return pow(3,times);
       if(left==1) return pow(3,times-1)*4;
       else return pow(3,times)*2;
    }
};

思路2 动态规划解法

  1. 我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来
  2. 用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
  3. 我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪
  4. 剪了第一段后,剩下 ( i − j ) (i - j) (ij)长度可以剪也可以不剪。如果不剪的话长度乘积即为 j ∗ ( i − j ) j * (i - j) j(ij);如果剪的话长度乘积即为 j ∗ d p [ i − j ] j * dp[i - j] jdp[ij]。取两者最大值 m a x ( j ∗ ( i − j ) , j ∗ d p [ i − j ] ) max(j * (i - j), j * dp[i - j]) max(j(ij),jdp[ij])
  5. 第一段长度j可以取的区间为 [ 2 , i ) [2,i) [2,i),对所有j不同的情况取最大值,因此最终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 * (i - j), j * dp[i - j])) dp[i]=max(dp[i],max(j(ij),jdp[ij]))
  6. 最后返回dp[n]即可.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

USTC暖暖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值