leetcode 343. Integer Break

来源:互联网 发布:淘宝怎么帮人代付 编辑:程序博客网 时间:2024/06/14 15:13

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.

For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).

Note: You may assume that n is not less than 2 and not larger than 58.

我就想,可以从n/2的元素开始考虑乘积,然后依次减一。如:

10

5 5 

4 4 2

3 3 3 1  不对

3 3 4  


11

5 6

4 4 4 3

3 3 3 2 

3 3 5     不对


16

8 8 

7 7 2

6 6 4  

5 5 6

4 4 4 4 

3 3 3 3 4 

2 2 2 2 2 2 2 2 

可以看到3 3 3 1不对,而3 3 3 4对,说明我们不仅仅要分解,到了最后部分还要看看是分解了乘积大,还是合起来乘积大。

package leetcode;public class Integer_Break_343 {public int integerBreak(int n) {int left=n/2;int product=left*(n-left);while(left>2){left--;int pow=n/left;int yu=n%left;int thisProduct1=(int)Math.pow(left, pow-1)*(yu+left);int thisProduct2=(int)Math.pow(left, pow)*yu;int thisMaxProduct=thisProduct1>thisProduct2?thisProduct1:thisProduct2;if(thisMaxProduct>=product){product=thisMaxProduct;}else{break;}}return product;}public static void main(String[] args) {// TODO Auto-generated method stubInteger_Break_343 i=new Integer_Break_343();System.out.println(i.integerBreak(10));}}
这里的thisMaxProduct就是考虑是分解了乘积大,还是合起来乘积大,取大的哪一个。一旦当前left所能构成的最大乘积已然小于之前得到的最大乘积,那么left--只会越来越小,因此可以直接break,不用循环了。

大神则是发现了这道题背后的数学道理:

首先我们来思考:如果我们把N分解成2个数,那么最大的乘积是多少?
我使用一个函数来表示这个乘积: f=x(N-x)
那么,当N为偶数时,最大值是 (N/2)*(N/2) ;当N为奇数时,最大值是 (N-1)/2 *(N+1)/2。
所以,如果当f的最大值能够大于N时,我们就可以将N进行这样的分解。那么,f>=N的条件是什么呢?
(N/2)*(N/2)>=N, then N>=4
(N-1)/2 *(N+1)/2>=N, then N>=5
这2个表达式说明,乘数应该要小于4。因为倘若乘数大于等于4了,那么我们就可以将乘数分解以获得更大的乘积。那么乘数只有可能是1,2,3,很显然1要排除在外。那么最大乘积中的乘数应当只有2或者3。
但是我们要尽量用3,是因为
对于6来说,3 * 3>2 * 2 * 2。因此最优解乘法中,应该最多不超过两个2。

public class Solution {    public int integerBreak(int n) {        if(n==2) return 1;        if(n==3) return 2;        int product = 1;        while(n>4){            product*=3;            n-=3;        }        product*=n;                return product;    }}
另一个大神也是相同的想法。

if n = a*3 then the answer will just be 3^a.
if n = a*3 + 2 then the answer will be 2*(3^a).
and if n = a*3 + 2+2 then the answer will be 2 * 2 * 3^a     //这个样子是不是更容易理解呢?

public class Solution {    public int integerBreak(int n) {        if(n == 2)            return 1;        else if(n == 3)            return 2;        else if(n%3 == 0)            return (int)Math.pow(3, n/3);        else if(n%3 == 1)            return 2 * 2 * (int) Math.pow(3, (n - 4) / 3);        else             return 2 * (int) Math.pow(3, n/3);    }            }
话说回来,这个结论可是有严谨的数学证明的哦!

我们假设 n 足够大,能够被分成任意更小的正实数。我们现在试着计算什么实数能够产生最大的乘积。
我们把 n 分解成 (n / x) 个X, 那么乘积将会是 xn/x, 并且我们希望使它最大。

求它的导数,得到 n * xn/x-2 * (1 - ln(x)).
导数>0时, 0 < x < e;导数= 0 时 x = e;导数<0时, x > e,
这意味着当x<e时,乘积呈上升趋势;当x=e时到达顶峰;当x>e时,乘积开始往下降。 

这展示了一个事实:当 n 足够大时,我们应当将 n 分解成实数。最优解就是将它分解为最好都是e(the best idea is to break it into nearly all e's.)
换句话说,如果我们只能将 n 分解成整数, 我们应当选择最靠近 的整数。
候选整数为 2 and 3 ,因为 2 < e < 3, 但是我们通常更倾向于选择 3,为什么呢?

因为6 = 2 + 2 + 2 = 3 + 3. 但是 2 * 2 * 2 < 3 * 3.

所以,如果在分解中有三个2的话,我们可以将其替代为两个3,以得到更大的乘积。

这上面所有的分析都依赖于 n is significantly large. When n is small (say n <= 10), 该结论会出错.
比如, 当 n = 4, we have 2 * 2 > 3 * 1.
To fix it, we keep breaking n into 3's until n gets smaller than 10, then solve the problem by 暴力.

然后还有个大神嫌弃这个解法思维太复杂了,他的想法如下:

如果有一个分解包含乘数f >= 4,那么你可以将它换成乘数 2 and f-2,因为2*(f-2) = 2f-4 >= f,所以你永远都不需要一个>=4的乘数。意味着你只需要1,2,3。1当然不可能,那么我们应该更优先选择3,因为3*3 is simply better than 2*2*2,所以我们不该使用2超过两次。


原创粉丝点击