剑指Offer 14.剪绳子—动态规划、数学推导


题目

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

思路

(1)数学推导

从数学上推导,可以得出此题的结论:若想得到最大乘积,绳子剪成小段的长度只能是2或3,且最多有两个2。因此本题剪绳子的原则可以确定为:至多两个2,其余都是3。

证明:
假设绳子长度为N, 剪成m段, 长度分别为 n 1 n_{1} n1, n 2 n_{2} n2, … n m n_{m} nm,即 N = n 1 + n 2 + . . . + n m N = n_{1} + n_{2} + ... + n_{m} N=n1+n2+...+nm,以下证明前述结论。

  1. 证明 n i < 5 n_{i}<5 ni<5 :

    n i ≥ 5 n_{i} \geq 5 ni5,则此段绳子一定不可以再剪使得乘积更大(大于5)。
    但是,
    n i ≥ 5 n_{i} \geq 5 ni5
    2 n i ≥ 10 2n_{i} \geq 10 2ni10,即 2 n i > 9 2n_{i} > 9 2ni>9
    3 n i − 9 > n i 3n_{i} - 9 > n_{i} 3ni9>ni,即 3 ( n i − 3 ) > n i 3(n_{i} - 3)> n_{i} 3(ni3)>ni,表示绳子长度还可以由 n i n_{i} ni剪下去使得乘积更大。与前述乘积不可更大矛盾,所以 n i ≥ 5 n_{i} \geq 5 ni5不成立,因此 n i < 5 n_{i}<5 ni<5

  2. 证明 n i ≠ 4 n_{i} \neq 4 ni=4

    因为4完全可以由两个2代替,4 = 2 × 2

  3. 证明最多有两个2:

    目前已知,绳子长度只可为 1,2,3,而长度为6时,由于 2 × 2 × 2 = 8 < 3 × 3 = 9,所以3个2不是最优,长度为4时,2 × 2是最优。

综上,不管多长的绳子,要想取得最大乘积,需要尽可能剪成长度为3或2的小段,且至多有两个长度为2的段,剩余都是3。

(2)动态规划

  • 确定dp数组含义:dp[i]表示将长度为i的绳子剪成至少两段后的最大乘积
  • 状态转移方程:对长度为i的绳子剪出一段长度为 j (1<= j < i)的段,有两种方案
    • 剪成两段,将 i 剪成 j 和 i-j,此时 dp[i] = j × (i-j);
    • 剪成两段以上,将 i 剪成 j 与 i-j, i-j继续剪,dp[i] = j × dp[i-j]。
  • 确定base case:初始化 dp[2] = 1

代码实现

(1)数学

class Solution {
public:
    int cuttingRope(int n) {
        if (n <= 3) return n - 1;
        int res = 1;
        if (n % 3 == 1) {
            res *= 4;
            n -= 4;
        } else if (n % 3 == 2) {
            res *= 2;
            n -= 2;    
        } 
        while (n) res *= 3, n -= 3;
        return res;
    }
};

(2)动态规划

class Solution {
public:
    int cuttingRope(int n) {
        vector<int> dp(n + 1);
        dp[2] = 1;
        
        for (int i = 3; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]));
            }
        }

        return dp[n];
    }
};

总结

本文给出两种解法:
动态规划时间复杂度为 O ( n 2 ) O(n^2) O(n2), 空间复杂度 O ( n ) O(n) O(n);
数学推导时间复杂度更低,为对数级 O ( l o g n ) O(logn) O(logn), 空间复杂度 O ( 1 ) O(1) O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值