剑指 Offer 14- I. 剪绳子 你一根长度为n的绳子,请把绳子剪成整数长的m段 ,每段绳子的长记为 k[0],k[1].k[m-1] 请问 k[0]*k[1]*..*k[n]最大乘积

package SwordOffer;
/** 
* @Description: 给你一根长度为 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
注意:本题与主站 343 题相同:https://leetcode-cn.com/problems/integer-break/

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
* @Param:  
* @return:  
* @Author: lvhong
* @Date:  
* @E-mail lvhong282@163.com
*/ 
public class lab14middle {
    //选择3的数据推导过程

    //复杂度分析:
    //时间复杂度 O(1) : 仅有求整、求余、次方运算。求整和求余运算:资料提到不超过机器数的整数可以看作是 O(1);幂运算:查阅资料,提到浮点取幂为 O(1)。
    //空间复杂度 O(1) : 变量 a 和 b 使用常数大小额外空间。

    //解题思路:
    //设将长度为 n 的绳子切为 a 段:
    //n=n1 +n2+...+na

    //本题等价于求解:
    //max(n1×n2×...×na)
    //
    //以下数学推导总体分为两步:① 当所有绳段长度相等时,乘积最大。② 最优的绳段长度为 3 。
    //数学推导:
    //以下公式为“算术几何均值不等式” ,等号当且仅当 n1 = n2 = ... = na时成立。
   //n1+n2+...+na≥a n1n2 ...na
    //
    //推论一: 将绳子 以相等的长度等分为多段 ,得到的乘积最大。
    //设将绳子按照 x 长度等分为 a 段,即 n = ax,则乘积为 x^a。观察以下公式,由于 n 为常数,因此当 x^(1/x)取最大值时, 乘积达到最大值。
    //x^a = x^(n/x)=(x^(1/x))^n
    //根据分析,可将问题转化为求 y = x^(1/x)的极大值,因此对 xx 求导数。
    //      lny=(1/x)^Inx     取对数
    //(1/y)y˙=1/(x^2) - (1/(x^2))Inx  对x求导'
    //        =(1-Inx)/x^2
    //整理得y˙=((1-Inx)/x^2)*(x^(1/x))


    //令 =0 ,则 1 - ln x = 0,易得驻点为 x0 = e≈2.7 ;根据以下公式,可知 x0为极大值点。
    //y˙{>0,x∈[−∞,e)
    //y˙{<0,x∈(e,∞]
​
    //由于切分长度 x 必须为整数,最接近 e 的整数为 2 或 3 。如下式所示,代入 x = 2和 x = 3,得出 x = 3,乘积达到最大。
    //y(3) = 3^(1/3)  ≈1.44
    //y(2) = 2^(1/2)  ≈1.41
    //口算对比方法:给两数字同时取 6 次方,再对比。
    //[y(3)]^6 = (3^{1/3})^6 = 9
    //[y(2)]^6 = (2^{1/2})^6 = 8


    //推论二: 尽可能将绳子以长度 33 等分为多段时,乘积最大。
    //
    //切分规则:
    //最优: 3 。把绳子尽可能切为多个长度为 3 的片段,留下的最后一段绳子的长度可能为 0,1,2三种情况。
    //次优: 2 。若最后一段绳子长度为 2 ;则保留,不再拆为 1+1 。
    //最差: 1 。若最后一段绳子长度为 1 ;则应把一份 3 + 1替换为 2 + 2,因为2×2>3×1。


    //算法流程:
    //当n≤3 时,按照规则应不切分,但由于题目要求必须剪成 m>1段,因此必须剪出一段长度为 1 的绳子,即返回 n - 1。
    //当 n>3时,求 n 除以 3 的 整数部分 a 和 余数部分 b (即 n = 3a + bn=3),并分为以下三种情况:
    //当 b = 0 时,直接返回 3^a;
    //当 b = 1 时,要将一个 1 + 3转换为 2+2,因此返回 3^(a−1)×4;
    //当 b = 2时,返回 3^a×2。


    //
    //作者:jyd
    //链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/
    //来源:力扣(LeetCode)
    //著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    // public int cuttingRope(int n) {
    //     if(n <= 3) 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;
    // }


    //动态规划
    //对于的正整数 n,当 n≥2 时,可以拆分成至少两个正整数的和。令 k 是拆分出的第一个正整数,则剩下的部分是 n−k,n−k 可以不继续拆分,或者继续拆分成至少两个正整数的和。由于每个正整数对应的最大乘积取决于比它小的正整数对应的最大乘积,因此可以使用动态规划求解。
    //dp数组的含义: dp[i] 表示将正整数 i 拆分成至少两个正整数的和之后,这些正整数的最大乘积。
    //边界条件: 0 不是正整数,1 是最小的正整数,0 和 1 都不能拆分,因此 dp[0]=dp[1]=0。
    //状态转移方程:
    //当 i≥2 时,假设对正整数 i 拆分出的第一个正整数是 j(1≤j<i),则有以下两种方案:
    //将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j×(i−j);
    //将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j×dp[i−j]。
    //因此,当 j 固定时,有 dp[i]=max(j×(i−j),j×dp[i−j])。由于 j 的取值范围是 1 到 i−1,需要遍历所有的 j 得到 dp[i] 的最大值,因此可以得到状态转移方程如下:
    //dp[i]= max{1≤j<i} {(j×(i−j),j×dp[i−j])}

    //作者:Sophia_fez
    //链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/dong-tai-gui-hua-shu-xue-by-sophia_fez/
    //来源:力扣(LeetCode)
    //著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

    //优化动态规划
    //方法一中定义的状态转移方程如下:dp[i]= max{1≤j<i} {(j×(i−j),j×dp[i−j])}
    //使用上述状态转移方程,计算dp[i] 时,j 的值遍历了从 1 到 i-1 的所有值,因此总时间复杂度是 O(n^2)是否可以降低时间复杂度?
    //
    //上述状态转移方程包含两项,当 j 固定时,dp[i] 的值由j×(i−j) 和 j×dp[i−j] 中的较大值决定,因此需要对两项分别考虑。
    //首先考虑j×dp[i−j] 这一项。
    //注意到dp 的定义,dp[i] 表示将正整数 i 拆分成至少两个正整数的和之后,这些正整数的最大乘积,
    // 因此对于任意1≤j<i,有dp[i]≥j×dp[i−j]。
    
    //dp[i]=max(2×(i−2),2×dp[i−2],3×(i−3),3×dp[i−3])
    //
    //作者:LeetCode-Solution
    //链接:https://leetcode-cn.com/problems/integer-break/solution/zheng-shu-chai-fen-by-leetcode-solution/
    //来源:力扣(LeetCode)
    //著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    public int cuttingRope(int n) {
        if (n < 4) {
            return n - 1;
        }
        int[] dp = new int[n + 1];
        dp[2] = 1;
        for (int i = 3; i <= n; i++) {
            dp[i] = Math.max(Math.max(2 * (i - 2), 2 * dp[i - 2]), Math.max(3 * (i - 3), 3 * dp[i - 3]));
        }
        return dp[n];
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值