剪绳子 /整数拆分 4种解法


 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
思路:我们设 f ( n ) f(n) f(n)是将n拆分后的所能得到的最大值,则 f ( n ) = m a x ( 1 ∗ f ( n − 1 ) , 2 ∗ f ( n − 2 ) , . . . ) f(n)=max(1*f(n-1),2*f(n-2),...) f(n)=max(1f(n1),2f(n2),...),我们也可以画出他的递归树
在这里插入图片描述
则我们设 r e s res res为最大值,则 r e s = m a x ( r e s , i ∗ i n t e g e r B r e a k ( n − i ) ) res=max(res,i*integerBreak(n-i)) res=max(res,iintegerBreak(ni)),但有时候n是不需要继续划分的,比如,当 n = 2 n=2 n=2时, 2 ∗ f ( n ) > 1 ∗ 1 ∗ f ( n ) 2*f(n)>1*1*f(n) 2f(n)>11f(n),所以我们还要比较 i ∗ ( n − i ) i*(n-i) i(ni)。所以 r e s = m a x ( r e s , m a x ( i ∗ ( n − i ) , i ∗ i n t e g e r B r e a k ( n − i ) ) ) ; res = max(res, max(i * (n - i), i * integerBreak(n - i))); res=max(res,max(i(ni),iintegerBreak(ni)));

1 递归

int integerBreak(int n) {
     //解法1 递归
         if(n==2)
         return 1;
      int res=-1;
      for(int i=1;i<n;i++)
      {
      	res = max(res, max(i * (n - i), i * integerBreak(n - i)));
      }
      return res;
    }

大量重复计算,时间超时。

2 带备忘录的递归

对递归进行优化

 int integerBreak(int n) {
     //解法2 带备忘录的递归
         if(n==2)
         return 1;
         vector<int> memo(n+1,0);
       int res=-1;
       if(memo[n]!=0)
       return memo[n];
       for(int i=1;i<n;i++)
       {
       	res = max(res, max(i * (n - i), i * integerBreak(n - i)));
       }
       memo[n]=res;
       return res;
    }

3 动态规划

时间复杂度为 O ( 3 n 2 ) O(3n^2) O(3n2),空间复杂度为 O ( n ) O(n) O(n)

int integerBreak(int n) {
     //解法3 动态规划 dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]))
         if(n==2)
         return 1;
         vector<int> dp(n+1,0);
         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];
    }

4 贪心(数学方法)

这是大佬总结的,应尽可能的把n分为3的和,然后再拆成更多的 2,这样就能保证拆出来的整数的乘积结果最大。但我们也应该注意,当拆分为 7 = 3 + 3 + 1 7=3+3+1 7=3+3+1是没有 7 = 3 + 2 + 2 7=3+2+2 7=3+2+2大的 ( 3 ∗ 3 = 9 ) < ( 3 ∗ 2 ∗ 2 = 12 ) (3*3=9)<(3*2*2=12) 33=9)<(322=12
所以应分为三种情况讨论:
1、$ n%3==0$ ,则直接将n分为 n / 3 n/3 n/3 3 3 3,求 3 n / 3 3^{n/3} 3n/3

2、$ n%3==1 $ ,则直接将n分为 ( n − 1 ) / 3 − 1 (n-1)/3-1 (n1)/31 3 3 3,和俩个2,求 3 n / 3 − 1 + 2 ∗ 2 3^{n/3-1}+2*2 3n/31+22

3、$ n%3==2$ ,则直接将n分为 ( n − 2 ) / 3 (n-2)/3 (n2)/3 3 3 3,求 3 ( n − 2 ) / 3 ∗ 2 3^{(n-2)/3}*2 3(n2)/32

     int integerBreak(int n) {
          if (n == 2) return 1;
          if (n == 3) return 2;//这里3必须进行特判,不然b=0,返回的是3^1=3,这个结果是没有进行划分
         int b=n%3;
         if(b==0)
            {
                int a=n/3;
                return pow(3,a);
            }
         if(b==1)
            {
                int a=(n-1)/3;
                return pow(3,a-1)*4;
            }
         int a=(n-2)/3;
                return pow(3,a)*2;
     }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值