【JZ-14-I】剪绳子(数学、动态规划)

题目

在这里插入图片描述
参考1

参考2

方法一-动态规划

用一个数组 dp 记录从 0 到 n 长度的绳子剪掉后的最大乘积,即 d p [ i ] dp[i] dp[i] 表示长度为 i 的绳子剪成 m 段后的最大乘积,初始化 d p [ 2 ] = 1 dp[2] = 1 dp[2]=1,最终返回 d p [ n ] dp[n] dp[n](数组大小需要设为 n + 1 n+1 n+1)。
假设已经减掉了长度为 j 的第一段,为了使得最后的乘积最大,从长度为2开始剪,即第一段长度 j 的取值范围为 [ 2 , i ) [2,i) [2,i)。剩下的 ( i - j )长度的绳子可以剪也可以不剪,不剪的话长度乘积为 j ∗ ( i − j ) j*(i-j) j(ij);剪的话长度乘积为 j ∗ d p [ i − j ] j*dp[i-j] jdp[ij]。为了使得长度乘积最大,我们取两者中的最大值。
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*(i-j), j*dp[i-j])) dp[i]=max(dp[i],max(j(ij),jdp[ij]))

class Solution {
    public int cuttingRope(int n) {
        int[] dp = new int[n+1];
        dp[2] = 1;
        for(int i = 3; i < n + 1; i++){
            for(int j = 2; j < i; j++){
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
}

notes:
这里的转移方程里还考虑了 d p [ i ] dp[i] dp[i] ,是因为 j j j的取值是不确定的,不同的 j j j会有不同的结果。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)

方法二-贪心算法(数学)

算数-几何平均值不等式可知,剪成相等长度的m段时乘积最大。
接下来考虑相等的每一段的长度为多少时乘积最大。设该长度为 x x x,分为 a a a段, n = a x n=ax n=ax,则乘积为 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 n n为常数,那么要求 x a x^a xa的最大值,也就是 y = x 1 x y=x^\frac{1}{x} y=xx1的最大值
两边取对数、对x求导后可得驻点 x 0 = e ≈ 2.7 x_0=e\approx2.7 x0=e2.7,且 d y d x { > 0 , x ∈ [ − ∞ , e ) < 0 , x ∈ ( e , ∞ ] \frac{dy}{dx}\begin{cases}>0,\quad x \in[-\infty,e)\\<0, \quad x \in(e,\infty]\end{cases} dxdy{>0,x[,e)<0,x(e,]取最接近 e 的整数2或3,计算可知 x = 3 x=3 x=3时乘积最大。
也就是说,尽可能将绳子以长度3等分为多段时,总的长度乘积最大
切分时会有3种可能情况:

  1. 刚好能剪成若干个长度为3的段
  2. 最后剩下长度为2的一段,此时应保留该段,不再继续剪成两个长度为1的段
  3. 最后剩下长度为1的一段,此时应将上一个长度为3的段跟这一段合并(4 > 3 * 1)

具体算法流程:

  1. n = 2时返回1,n = 3时返回2,即n < 4时返回n -1
  2. n >= 4时,计算出 n = 3 a + b n=3a+b n=3a+b中的 a 和 b:
    b = 0时,直接返回 3 a 3^a 3a
    b = 1时,返回 3 a − 1 × 4 3^{a-1}\times4 3a1×4;
    b = 2时,返回 3 a × 2 3^a\times2 3a×2
class Solution {
    public int cuttingRope(int n) {
        if( n < 4)return n - 1;
        int a = n / 3, b = n % 3;
        if(b == 0)return (int)Math.pow(3, a);
        if(b == 1)return (int)Math.pow(3, a - 1) * 4;
        return (int)Math.pow(3, a) * 2;
    }
}
  • 时间复杂度: O ( 1 ) O(1) O(1),只有取整、取余、次方运算,注意math.pow()调用C库的pow()函数,执行浮点取幂,时间复杂度为 O ( 1 ) O(1) O(1)
  • 空间复杂度: O ( 1 ) O(1) O(1)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值