剑指Offer-剪绳子

剑指Offer-剪绳子

题目描述

LCR 131. 砍竹子 I

现需要将一根长为正整数 bamboo_len 的竹子砍为若干段,每段长度均为正整数。请返回每段竹子长度的最大乘积是多少。

示例 1:

输入: bamboo_len = 12
输出: 81

提示:

  • 2 <= bamboo_len <= 58

思路

参考了力扣的两位大神的思路。

数学解法

通过数学可证明

设n = n1+n2+n3…n,使得n1 * n2 * n3…*n 最大,有结论: 1. 当n1= n2 = n3 = …n 时乘积最大 2. 当每段n1=3时,乘积最大

证明过程省略

代入此题可知,我们需要将竹子分成n段长度为3的小竹子,但是竹子总长不一定是3的倍数,因此最后一段竹子的长度有以下可能:

  • 1:此时最大乘积应该由 pow(3,n) 变成pow(3,n-1)*4,原因是需要将最后一段长度为1的竹子与上一段长度为三的竹子合体,因为2x2 > 3x1
  • 2:直接x2,返回pow(3,n-1)*2

另外一种特殊情况则是总长度一开始便不满足3的长度,题目限制了最小为2,因此如果长度为2时,返回1,长度为3时返回2即可。

代码如下

class Solution {
public:
    int cuttingBamboo(int bamboo_len) {
        if(bamboo_len <= 3)return bamboo_len-1;
        int b = bamboo_len%3;
        int a = bamboo_len/3;
        if(b == 0)return pow(3,a);
        if(b == 1)return 4*pow(3,a-1);
        return 2*pow(3,a);
    }
};

动态规划

按照动态规划五部曲,有如下操作:

  1. 状态方程:dp[i]: 代表长度为i的竹子被分成m段的最大乘积
  2. 状态转移:dp[i] = max(dp[i], max(dp[j] * (i-j), j * (i-j)))
  3. 遍历:正序遍历,2<= i <=bamboo_len 1<= j
  4. 初始化:dp[0] = d[1] = 0 , 因为长度0和1都不可以继续划分,或者dp[1]也可以理解为分成长度为1和长度为0的部分,因此dp[1] 也可以=1
  5. 最后返回:dp[n]

代码如下

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

你可能感兴趣的:(算法,算法)