343.整数拆分

题目

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。

思路

这道题其实就是剑指offer上的剪绳子问题,有动态规划和贪心算法两种解法。

动态规划

dp[i]表示正整数i的最大乘积,则dp[i]=max{dp[i-1]*1, (i-1)*1, dp[i-2]*2, (i-2)*2, ... , dp[i-(i-1)]*(i-1), (i-(i-1))*(i-1)};
之所以还要进行dp[i-j]*j和(i-j)*j的比较是因为可能i只进行一次划分,得到i-j不继续划分了

   public int integerBreak(int n) {
        int[] dp=new int[n+1];
        //1不能被划分
        dp[1]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;++j)
                dp[i]=Math.max(Math.max(dp[i-j]*j,j*(i-j)),dp[i]);
        return dp[n];
    }

其实由下面贪心算法部分的分析可知,当n>=4时,划分得到结果都是大于等于不划分的结果的,那么此时就没有必要进行dp[i-j]*j和(i-j)*j的比较了,于是可以有下面的写法。
注意这里的dp[i]并不是子问题的结果,因为题目中要求了必须拆分为至少两个正整数的和,这里的dp[i]并没有保证一定会划分,例如dp[2]=2而不是dp[2]=1*1=1)。因为n<4时,dp[i]都是没有经过划分的结果,所以n=2,n=3时需要单独返回。

  public int integerBreak(int n) {
        if(n==2) return 1;
        if(n==3) return 2;
        int[] dp=new int[n+1];
        //i<4时,dp[i]是没有经过划分的结果
        dp[2]=2;
        dp[3]=3;
        //i>=4时,dp[i]都是划分得到的结果
        for(int i=4;i<=n;++i){
            for(int j=2;j<i;++j){
                dp[i]=Math.max(dp[i],dp[j]*(i-j));
            }
        }
        return dp[n];
  }

贪心算法

2*(n-2)>=n,解得n>=4。也就是说大于等于4的正整数都可以继续划分,故划分出来的正整数不能大于3。又显然有1*(n-1)<n,故划分出来的整数要么是2,要么是3

那么是优先划分出来3,还是划分出来2呢?

3*(n-3)>=2*(n-2),解得n>=5。即n>=5时,优先划分出3

总结如下:

n>=5时,优先划分出3;
n=4时,优先划分出2(其实可以直接返回结果4);
n<=3时,不划分。但题目要求拆分为至少两个正整数的和,那么n<=3时的情况可以单独返回。

  public int integerBreak(int n) {
        if(n==2) return 1;
        if(n==3) return 2;
        if(n==4) return 4;
        //默认n>=5,这时优先划分出3
        int numOf3=n/3;
        //划分到只剩下4的时候应该停止划分出3,因为3*(n-3)>=2*(n-2)的前提条件是n>=5
        if(n-numOf3*3==1) numOf3--;
        int numOf2=(n-numOf3*3)/2;
        return (int)Math.pow(3,numOf3)*(int)Math.pow(2,numOf2);
  }

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/integer-break
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

原文地址:https://www.cnblogs.com/Frank-Hong/p/14595706.html