整数拆分
描述
中等
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
解题
1.动态规划
- 当n为2时,只能分解成1+1,返回2
- 当n为3时,能分解成1+1+1或1+2,返回2
设dp[i]为将i拆分为至少2个正整数后,这些整数乘起来可以得到的乘积
假设1~i之间有一个数j将i分解成j和i-j,那么dp[i]等于j和i-j的乘积
然后1~i-j之间有没有数将其分解成两部分,使得*dp[i-j]*等于两个数之和,不确定
好像找到转移方程了
当i-j不再继续拆分时,例如6拆成3+3
d
p
[
i
]
=
j
∗
(
i
−
j
)
dp[i] =j*(i-j)
dp[i]=j∗(i−j)
当i-j继续拆分时,例如6拆成3+2+1
d
p
[
i
]
=
j
∗
d
p
[
i
−
j
]
dp[i]=j*dp[i-j]
dp[i]=j∗dp[i−j]
因此其中取较大的数
d
p
[
i
]
=
j
∗
m
a
x
(
i
−
j
,
d
p
[
i
−
j
]
)
dp[i] = j*max(i-j,dp[i-j])
dp[i]=j∗max(i−j,dp[i−j])
然后在遍历j时找到最大的dp[i]
class Solution:
def integerBreak(self, n: int) -> int:
if n <= 3:
return n - 1
dp = [0 for i in range(n + 1)]
for i in range(2, n + 1):
for j in range(1, i):
dp[i] = max(dp[i], j * max(i - j, dp[i - j]))
return dp[n]
2.数学方法
将n分成多个数之和
n
=
n
1
+
n
2
+
.
.
.
+
n
a
n = n_1+n_2+...+n_a
n=n1+n2+...+na
然后求解乘积(记作res)
r
e
s
=
m
a
x
(
n
1
∗
n
2
∗
.
.
.
∗
n
a
)
res=max(n_1*n_2*...*n_a)
res=max(n1∗n2∗...∗na)
根据算术几何平均不等式
n
1
+
n
2
+
.
.
.
+
n
a
a
≥
n
1
∗
n
2
∗
.
.
.
∗
n
a
a
\frac{n_1+n_2+...+n_a}{a} \geq \sqrt[a]{n_1*n_2*...*n_a}
an1+n2+...+na≥an1∗n2∗...∗na
当且仅当
n
1
=
n
2
=
.
.
.
=
n
a
时
,
不
等
式
取
等
号
n_1=n_2=...=n_a时,不等式取等号
n1=n2=...=na时,不等式取等号
所以,当分解出来的数都相同时,其乘积最大
那么问题来了,需要分解成多少个相同的数
假设分成a个数,每个数为x,则
n
=
a
x
n = ax
n=ax
那么最后的乘积为
r
e
s
=
x
a
=
x
n
x
=
(
x
1
x
)
n
res=x^a=x^{\frac{n}{x}}={(x^{\frac{1}{x}})}^n
res=xa=xxn=(xx1)n
可以转化为求y的最大值
y
=
x
1
x
y=x^{\frac{1}{x}}
y=xx1
取对数后求导
l
n
(
y
)
=
1
x
l
n
(
x
)
ln(y) = \frac{1}{x}ln(x)
ln(y)=x1ln(x)
1 y y ˙ = − 1 x 2 l n ( x ) + 1 x 2 = 1 − l n ( x ) x 2 \frac 1 y \dot y=-\frac 1 {x^2}ln(x)+\frac 1 {x^2}=\frac{1-ln(x)}{x^2} y1y˙=−x21ln(x)+x21=x21−ln(x)
y ˙ = 1 − l n ( x ) x 2 ∗ y = 1 − l n ( x ) x 2 x 1 x \dot{y}=\frac{1-ln(x)}{x^2}*y=\frac{1-ln(x)}{x^2}x^{\frac 1 x} y˙=x21−ln(x)∗y=x21−ln(x)xx1
令导数
y
˙
=
0
\dot{y}=0
y˙=0
得到
1
−
l
n
(
x
)
=
0
1-ln(x)=0
1−ln(x)=0
x = e ≈ 2.7 x = e \approx 2.7 x=e≈2.7
又因为
y
˙
=
1
−
l
n
(
x
)
x
2
x
1
x
=
k
(
1
−
l
n
(
x
)
)
{
>
0
,
x
<
e
=
0
,
x
=
e
<
0
,
x
>
e
\dot{y}=\frac{1-ln(x)}{x^2}x^{\frac 1 x}=k(1-ln(x))\begin{cases}> 0,&x< e\\=0,&x=e \\<0,& x>e \end{cases}
y˙=x21−ln(x)xx1=k(1−ln(x))⎩⎪⎨⎪⎧>0,=0,<0,x<ex=ex>e
其中k为正数
所以,当x取e时存在最大值(极大值)
由于x需要为正整数,x可以取2或3
y
(
2
)
=
2
1
2
y(2) = 2^{\frac{1}{2}}
y(2)=221
y ( 3 ) = 3 1 3 y(3)=3^{\frac 1 3} y(3)=331
同时取6次方
(
y
(
2
)
)
6
=
(
2
1
2
)
6
=
2
3
=
8
{(y(2))}^6=(2^{\frac 1 2})^6=2^3=8
(y(2))6=(221)6=23=8
( y ( 3 ) ) 6 = ( 3 1 3 ) 6 = 3 2 = 9 {(y(3))}^6=(3^{\frac 1 3})^6=3^2=9 (y(3))6=(331)6=32=9
所以
y
(
3
)
>
y
(
2
)
y(3)>y(2)
y(3)>y(2)
所以,x取3
所以,将正整数n尽可能分解为3多的情况
那么就有3种情况
- 刚好将n分为3+3+…+3,即n是3的倍数
- 将n分为3+3+…+3+1,即n是3的倍数+1
- 将n分为3+3+…+3+2,即n是3的倍数+2
那么针对的就有3种计算方式
- 全部乘起来
- 第一部分将最后一个3和1,加起来成4,在分成2+2,积为4,第二部分将剩余的3乘起来,最后两部分相乘
- 将所有的3乘起来再乘上最后的2
class Solution:
def integerBreak(self, n: int) -> int:
if n <= 3:
return n - 1
a, b = n // 3, n % 3
if b == 0:
return int(math.pow(3, a))
elif b == 1:
return int(math.pow(3, a - 1) * 4)
elif b == 2:
return int(math.pow(3, a) * 2)
参考:leetcode题解