题目
方法一-动态规划
用一个数组 dp 记录从 0 到 n 长度的绳子剪掉后的最大乘积,即
d
p
[
i
]
dp[i]
dp[i] 表示长度为 i 的绳子剪成 m 段后的最大乘积,初始化
d
p
[
2
]
=
1
dp[2] = 1
dp[2]=1,最终返回
d
p
[
n
]
dp[n]
dp[n](数组大小需要设为
n
+
1
n+1
n+1)。
假设已经减掉了长度为 j 的第一段,为了使得最后的乘积最大,从长度为2开始剪,即第一段长度 j 的取值范围为
[
2
,
i
)
[2,i)
[2,i)。剩下的 ( i - j )长度的绳子可以剪也可以不剪,不剪的话长度乘积为
j
∗
(
i
−
j
)
j*(i-j)
j∗(i−j);剪的话长度乘积为
j
∗
d
p
[
i
−
j
]
j*dp[i-j]
j∗dp[i−j]。为了使得长度乘积最大,我们取两者中的最大值。
故
d
p
[
i
]
dp[i]
dp[i]的转移方程为:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
m
a
x
(
j
∗
(
i
−
j
)
,
j
∗
d
p
[
i
−
j
]
)
)
dp[i]=max(dp[i], max(j*(i-j), j*dp[i-j]))
dp[i]=max(dp[i],max(j∗(i−j),j∗dp[i−j]))
class Solution {
public int cuttingRope(int n) {
int[] dp = new int[n+1];
dp[2] = 1;
for(int i = 3; i < n + 1; i++){
for(int j = 2; j < i; j++){
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];
}
}
notes:
这里的转移方程里还考虑了
d
p
[
i
]
dp[i]
dp[i] ,是因为
j
j
j的取值是不确定的,不同的
j
j
j会有不同的结果。
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( n ) O(n) O(n)
方法二-贪心算法(数学)
由算数-几何平均值不等式可知,剪成相等长度的m段时乘积最大。
接下来考虑相等的每一段的长度为多少时乘积最大。设该长度为
x
x
x,分为
a
a
a段,
n
=
a
x
n=ax
n=ax,则乘积为
x
a
=
x
n
x
=
(
x
1
x
)
n
x^a=x^\frac{n}{x}=(x^\frac{1}{x})^n
xa=xxn=(xx1)n,其中
n
n
n为常数,那么要求
x
a
x^a
xa的最大值,也就是求
y
=
x
1
x
y=x^\frac{1}{x}
y=xx1的最大值。
两边取对数、对x求导后可得驻点
x
0
=
e
≈
2.7
x_0=e\approx2.7
x0=e≈2.7,且
d
y
d
x
{
>
0
,
x
∈
[
−
∞
,
e
)
<
0
,
x
∈
(
e
,
∞
]
\frac{dy}{dx}\begin{cases}>0,\quad x \in[-\infty,e)\\<0, \quad x \in(e,\infty]\end{cases}
dxdy{>0,x∈[−∞,e)<0,x∈(e,∞]取最接近 e 的整数2或3,计算可知
x
=
3
x=3
x=3时乘积最大。
也就是说,尽可能将绳子以长度3等分为多段时,总的长度乘积最大。
切分时会有3种可能情况:
- 刚好能剪成若干个长度为3的段
- 最后剩下长度为2的一段,此时应保留该段,不再继续剪成两个长度为1的段
- 最后剩下长度为1的一段,此时应将上一个长度为3的段跟这一段合并(4 > 3 * 1)
具体算法流程:
- n = 2时返回1,n = 3时返回2,即n < 4时返回n -1
- n >= 4时,计算出
n
=
3
a
+
b
n=3a+b
n=3a+b中的 a 和 b:
b = 0时,直接返回 3 a 3^a 3a;
b = 1时,返回 3 a − 1 × 4 3^{a-1}\times4 3a−1×4;
b = 2时,返回 3 a × 2 3^a\times2 3a×2
class Solution {
public int cuttingRope(int n) {
if( n < 4)return n - 1;
int a = n / 3, b = n % 3;
if(b == 0)return (int)Math.pow(3, a);
if(b == 1)return (int)Math.pow(3, a - 1) * 4;
return (int)Math.pow(3, a) * 2;
}
}
- 时间复杂度: O ( 1 ) O(1) O(1),只有取整、取余、次方运算,注意math.pow()调用C库的pow()函数,执行浮点取幂,时间复杂度为 O ( 1 ) O(1) O(1)
- 空间复杂度: O ( 1 ) O(1) O(1)