算法之剪绳子问题-动态规划and贪心思路

剪绳子问题


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


解题:动态规划

动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。

  1. 本题想要求的是不同情况下分绳子的乘积,假如我们要求长度为n的绳子拆分后的最大乘积,我们可以把它转化为长度为k和n-k长度绳子最大乘积相乘k*dp(n-k)的乘积。但是要注意,dp(n-k)是指n-k拆分为m段(m>1)时的最大长度,而如果这段绳子不拆分也有可能产生更大乘积。
  2. 得到表达式dp[n]=dp[k]*max(k,dp[n-k])
  3. k的取值从1开始,但是1不会扩大乘积,显然不能切出长度为1的绳子。
  4. k从2开始,一直到n/2都有可能产生更大的乘积,因为如果第一段长度大于n/2时,与小于n/2重复
class Solution {
public:
    int cuttingRope(int n) {
        vector<int> dp(n+1,1);
        if(n==3) return 2;
        for(int i=4;i<=n;i++){
            for(int j=2;j<=i/2;j++){
                dp[i]=max(dp[i],max((i-j)*j,dp[i-j]*j));
            }
        }
        return dp[n];
    }
};

贪心思路:

长为2的绳子,最大乘积应该是1*2,无拆分;

长为3的绳子,最大乘积是1*3,无拆分

长为4的绳子,最大乘积是1 * 4=2 * 2,无拆分和拆分结果相等

长为5的绳子,最大乘积是2 * 3=6,拆分结果更大

如何让绳子分得的乘积更大?每一小段乘积最大且小段数最多

长为6的绳子可以划分为3*3=9和2 *2 *2=8,划分成两段3更大

长为9的绳子可以划分为3*3 *3 =27和2 *2 *2 *2 *1=16

推测划分为最多的3,余数用1,2补充时乘积最大

如果题目增加一个限制条件

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

那么无法直接使用动态规划(取余数影响大小的比较),此时使用公式法更方便

class Solution {
public:
    int cuttingRope(int n) {
        return n<=3?n-1:(int)process(n);
    }
    long process(long n){
        return n>4?(process(n-3)*3)%1000000007 : n;
    }
};

n<=3情况比较特殊,n=1,n=2,n=3时结果分别为1,1,2(至少拆成两段)

猜你喜欢

转载自blog.csdn.net/weixin_44738872/article/details/116177213