栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

剪绳子
    • 题目
    • 思路
      • (1)数学推导
      • (2)动态规划
    • 代码实现
    • 总结


题目

给你一根长度为 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 ni​≥5,则此段绳子一定不可以再剪使得乘积更大(大于5)。
    但是,
    ∵ n i ≥ 5 n_{i} geq 5 ni​≥5
    ∴ 2 n i ≥ 10 2n_{i} geq 10 2ni​≥10,即 2 n i > 9 2n_{i} > 9 2ni​>9
    ∴ 3 n i − 9 > n i 3n_{i} - 9 > n_{i} 3ni​−9>ni​,即 3 ( n i − 3 ) > n i 3(n_{i} - 3)> n_{i} 3(ni​−3)>ni​,表示绳子长度还可以由 n i n_{i} ni​剪下去使得乘积更大。与前述乘积不可更大矛盾,所以 n i ≥ 5 n_{i} geq 5 ni​≥5不成立,因此 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 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)。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/957716.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号