剑指Offer14-Ⅰ.剪绳子

  • 剑指Offer14-Ⅰ.剪绳子

  • 题目:
    本题和343.整数拆分一模一样;
    一根长为n的绳子剪成m段,求每段绳子乘积的最大值;
    m > 1 && n > 1;

  • 思路:
    1.把绳子尽可能分成长度为3的小段,乘积最大;O(n):前面都是常数操作,后面while处循环n/3次,O(1)
    证明:n = n1 + n2 + …,其中某一段长度为ni;
    若ni>=5,则分成2和3乘积更大;
    若ni4,则分成2和2乘积不变;
    若ni
    1,显然长度为1的段会使乘积更小,例如3分成1和2;
    若3段2的,不如2段3的大,因此3比2更好;
    因此,优先分为3;若n%3==1,则分出来一个2+2,乘积为4;若n%3 ==2,则分出来一个2;其余只剩下3的倍数;

class Solution {
public:
    int cuttingRope(int n) {//尽可能分成3,不够3的分成2
        if (n <= 3) return 1 * (n - 1); //题目说m>1,即最少分两段, 则n == 2时最大值是分成1和1,n==3时最大值是分成1和2;
        
        int res = 1;//乘积初始值
        //先把n变成3的倍数
        if (n % 3 == 1) res *= 4, n -= 4;//分出来4
        if (n % 3 == 2) res *= 2, n-= 2;//分出来2
        
        while (n) {//每3一段
            res *= 3;
            n -= 3;
        }
        
        return res;       
    }
};

2.DP:
设置一个数组dp[n+1],dp[ i ]存储绳子长度为i 时的最大乘积。依题意,绳子至少被剪一次,所以绳子长度最小为2。外层for循环从绳长为i=2的情况开始依次计算,直到计算到绳长为n的情况。(由于n==2时,递推式中会出现dp[1],但绳子至少分两段,不符合题意,因此直接算出dp[2]的值为1,外层循环i从3开始更佳)

内层for循环:当绳长为i时,由于已知至少剪一刀,我们索性假设第一刀剪在长度为j的位置(即第一段绳子长度为j)。剩下的那段长度为( i - j )的绳子就变成了“可剪可不剪”。那究竟是“不剪了”得到的乘积大呢,还是“继续剪余下的这段”得到乘积更大?我们不知道,所以需要两种情况都计算一下进行比较。其中,“不剪了”得到的乘积是j * ( i - j ),“继续剪”得到的乘积是j * dp[ i - j ]。取其中的较大值,就是“第一剪在j位置”能得到的最大乘积。而第一剪的所有可能位置是1,2,…,i-1。依次计算所有可能情况,取最大值即为dp[ i ]的值。

由上述过程可知,只有先依次计算出dp[2],dp[3],…的值,才能得到dp[n]的值。此为动态规划。

class Solution {
public:
    int cuttingRope(int n) {
    	//初始化 dp数组:
        //1、为了条理清晰,我们初始化数组长度为 n+1(0下标无用)
        //2、根据题意,初始化dp[2]=1,且i从3开始;
        vector<int> dp(n + 1);//dp[i]表示绳子长度为i时的最大乘积
        dp[2] = 1 * 1;
        for (int i = 3; i <= n; ++i) {//外层循环表示求绳子长度为i时的最大乘积dp[i]
            for (int j = 1; j <= i / 2; ++j) {//枚举长度为i的绳子的第一段的长度
                dp[i] = max(dp[i] , max(j * (i - j), j * dp[i - j]));//因为i-j<i,因此在计算dp[i]时需要用到的dp[i-j]一定在之前算过了
            }
        }
        return dp[n];      
    }
};
  • 总结:
    方法1很牛B,方法2的DP很好的贯彻了DP5步法的分析过程,特别是dp数组的下标及对应含义,和dp数组的初始化要分析清楚;

怕忘,再刷

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值