版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxm1306192988/article/details/82055899
题目描述
把一根绳子剪成多段,并且使得每段的长度乘积最大。
n = 2 return 1
(2 = 1 + 1)n = 10 return 36
(10 = 3 + 3 + 4)
题解:
方法一:贪心
尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。
这样可以使乘积最大。
证明:
当 n>5 时, 3(n - 3) - 2(n - 2) = n - 5 >= 0。
因此把长度大于 5 的绳子切成两段,令其中一段长度为 3 可以使得两段的乘积最大。
class Solution {
public int integerBreak(int n) {
if (n < 2)
return 0;
if (n == 2)
return 1;
if (n == 3)
return 2;
int timesOf3 = n / 3; //计算 3的个数
if (n - timesOf3 * 3 == 1) //如果最后出现长度为1的绳子,
timesOf3--; //就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合
int timesOf2 = (n - timesOf3 * 3) / 2; //把它们切成两段长度为 2 的绳子
return (int) (Math.pow(3, timesOf3)) * (int) (Math.pow(2, timesOf2));
}
}
方法二:动态规划
定义函数f(n)为把长度为n的绳子剪成若干段后各段长度乘积的最大值。
在剪第一刀时,我们有n-1种选择,也就是说第一段绳子的可能长度分别为1,2,3…..,n-1。
因此f(n)=max(f(i)*f(n-i)),其中0 < i < n。
这是一个自上而下的递归公式。由于递归会有大量的不必要的重复计算。一个更好的办法是按照从下而上的顺序计算,也就是说我们先得到f(2),f(3),再得到f(4),f(5),直到得到f(n)。
当绳子的长度为2的时候,只能剪成长度为1的两段,所以f(2) = 1,当n = 3时,容易得出f(3) = 2;
class Solution {
public int integerBreak(int n) {
if (n < 2)
return 0;
if (n == 2)
return 1;
if (n == 3)
return 2;
int[] f = new int[n + 1];
f[0] = 0;
f[1] = 1;
f[2] = 2;
f[3] = 3;
int result = 0;
for (int i = 4; i <= n; i++) {
int max = 0;
for (int j = 1; j <= i / 2; j++) {
int num = f[j] * f[i - j];
if (num > max) {
max = num;
}
}
f[i] = max;
}
return f[n];
}
}