Medium
Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.
Example 1:
Input: 2
Output: 1
Explanation: 2 = 1 + 1, 1 × 1 = 1.
Example 2:
Input: 10
Output: 36
Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36.
Note: You may assume that n is not less than 2 and not larger than 58.
题目大意:每个正整数都能拆分成至少两个大于零的数之和,给出一个正整数n,求出有这个正整数拆分出的数字的最大乘积。
方法:
因为不限制拆分的数字个数,所以一个正整数可以有很多种拆分方式,如果每一种情况都去计算乘积,然后比较得出最大值一定会浪费很多时间。我们考虑动态规划的方法。每个数字都可以拆分成两个数字,然后这两个数字再拆分成更小的两个数字,直至数字为2,因为数字2只有一种拆分情况,可将其设为初始边界条件。这样我们就不用考虑拆分成多个数字的情况,只要保留较小数字的结果来表示较大数字的结果就好了。然后我们对数字进行拆分来观察变化规律:
2 | 1*1 | 1 |
3 | 2*1 | 2 |
4 | 2*2 | 4 |
5 | 3*2 | 6 |
6 | 3*3 | 9 |
7 | 3*4 | 12 |
8 | 6*2 | 18 |
9 | 6*3 | 27 |
10 | 6*4 | 36 |
11 | 6*5 | 54 |
12 | 6*6 | 81 |
13 | 6*7 | 108 |
14 | 12*2 | 162 |
我多写个几个例子,因为直到这么多例子我才发现了一个规律,这里需要先注意一下,表中写的乘积的两个数字不是直接将两数相乘,这两个数字是可以继续拆分的,我们已知较小数字的结果,那么我们用他们的结果相乘就是我们的结果了。
我们将2作为初始条件,5及其以后的数字都可以用3(和3的倍数)乘以一个数字表示,比如说5~7这部分就是3*m,m的范围是2~(3+1),8~13这部分是6*m,m范围是2~(6+1),以此类推。2和3是最基础的数字,他们可以组合出所有单双数。因为2和3的结果比本身要小,所以在用小数字结果计算大数字结果的时候要注意判断,选择最大的那个数参与计算。
数字3和4从2得来,分别乘以1和2。由这些规律我们可以维护一个大小为n+1的向量dp,n的结果就是dp[n]。代码如下:
class Solution { public: int integerBreak(int n) { vector<int> dp(n + 1, 0); dp[2] = 1; int level = 2, cur = 1; int p = 0; for (int i = 3; i <= n; ++i) { if ((level == 2 && cur<=level) || (level > 2 && cur <= level + 1)) { dp[i] = max(dp[level],level) * max(dp[cur],cur); cur++; } else { level = 3 * pow(2, p++); cur = 2; i--; } } return dp[n]; } };
看到网上的博主分析,每个数字都拆分出3,然后直到剩下的数字是2或者3为止。也就是从5开始,每个数的结果就是数字中包含的3个个数个3相乘,然后乘以剩下的数中包含的2的个数个2相乘。
代码如下:
class Solution { public: int integerBreak(int n) { if(n==2 || n==3)return n-1; if(n==4)return n; n-=5; return (int)pow(3,n/3+1)*(n%3+2); } };