题目描述
// 力扣
// 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段
// (m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0]
// ,k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大
// 乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度
// 分别为2、3、3的三段,此时得到的最大乘积是18。
// 牛客
// 给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数
// ,n>1并且m>1,m<=n),每段绳子的长度记为k[1],...,k[m]。请问k
// [1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我
// 们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
题解
///////////////////////////// 动态规划 //////////////////////////
// 牛客
// 动态规划解不难,但是需要换个思路来理解。这时就可以把题目理解成,
// "找出整数n能够被其他小于n的正整数k[i](i>=0)加起来的所有组合(k[0],k[1],...,k[m]),
// 在这个过程中,再找出这些组合里乘积最大的,并返回这个乘积。"
// 把这个问题抽象出来,再动态规划求解:
// 对于整数n,n的组合数,
// 不仅是第一个组合数可以取任何值,第二个组合数也可以取任何值,
// 在动态规划题目《青蛙跳台阶》问题中,青蛙只能跳1阶或者2阶,
// 也就是说组合数只能要么是1,要么是2。
// 而在题目《变态跳台阶》问题中,这个青蛙可以跳1阶,也可以直接跳n阶
// 组合数可以取任意值。所以其实这道题的动态规划解,跟《变态跳台阶》问题很像。
// 但是中途要保存组合数最大的积,且组合数相加不超过n。
// 组合数可以取1,剩下n-1,有f(n-1)种可能
// 可以取2,剩下n-2,有f(n-2)种可能
// ...
// 且有f(n)=f(1)+f(2)+...+f(k1-1)+f(k1),(1+2+..+k1=n, 1<k1<n)
// f(k1)=f(1)+f(2)+..+f(k2-2)+f(k2-1),(1+2+..+k2=k1, 1<k2<k1)
// ...
// f(2)=1
// 力扣
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:35.3 MB, 在所有 Java 提交中击败了60.53%的用户
class Solution {
public int cuttingRope(int n) {
if (n < 2)
return 0;
if (n == 2)
return 1;
if (n == 3)
return 2;
int[] f = new int[n + 1];
f[1] = 1;
f[2] = 2;
f[3] = 3;
for (int i = 4; i <= n; i++) {
int max = 0;
for (int j = 1; j <= i/2; j++) {
if (max < f[j]*f[i-j])
max = f[j]*f[i-j];
}
f[i]=max;
}
return f[n];
}
}
// 执行用时:1 ms, 在所有 Java 提交中击败了42.32%的用户
// 内存消耗:35.3 MB, 在所有 Java 提交中击败了62.66%的用户
class Solution {
public int cuttingRope(int n) {
if (n < 2)
return 0;
int[] f = new int[n + 1];
f[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (f[j] <= j && f[i] < j*(i-j))
f[i] = j*(i-j);
if (f[j] > j && f[i] < f[j]*(i-j))
f[i] = f[j]*(i-j);
}
}
return f[n];
}
}
// 牛客
// 运行时间:12ms
// 占用内存:9732k
public class Solution {
public int cutRope(int n) {
if (n < 2) // 题目已经说了会大于等于2,但还是习惯了写这个
return 0;
int[] f = new int[n + 1];
f[1] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if (f[j] <= j && f[i] < j*(i-j))
f[i] = j*(i-j);
if (f[j] > j && f[i] < f[j]*(i-j))
f[i] = f[j]*(i-j);
}
}
return f[n];
}
}
//////////////////////////// 贪心算法 /////////////////////////////
// 需要记住的是:这类问题尽可能拆分出长度为3的绳子,
// 直到拆不出3,那就拆分成2,此时的总乘积就是最大的
// 力扣
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:35.5 MB, 在所有 Java 提交中击败了33.21%的用户
class Solution {
public int cuttingRope(int n) {
if (n < 2)
return 0;
if (n == 2)
return 1;
if (n == 3)
return 2;
int to3 = n / 3; // n/3,表示n中分理出to3这么多个3
// n如果能被3整除,那么to3=n/3, to3*3=n,所以n-to3*3==0
// 如果n不能被3整除,n-to3即为n/3的余数
// 如果n/3余数为1,则to3减1
// 如果n/3余数为2,则to3不变。(n/3,余数要么是1要么是2)
if (n - to3 * 3 == 1)
to3--;
// 如果n/3的余数(n - to3*3)为1,to3减1相当于放回一个3给(n - to3*3)
// 此时(n - to3*3)=4
// 如果n/3的余数(n - to3*3)为2,则to3不变,(n - to3*3)=2
int to2 = (n - to3 * 3) / 2;
// 分离好了2和3,直接返回3^to3 * 2^to2(to3个3相乘,乘to2个2相乘)
return (int) (Math.pow(3, to3)) * (int) (Math.pow(2, to2));
}
}
// 牛客
// 运行时间:12ms
// 占用内存:9716k
public class Solution {
public int cutRope(int n) {
if (n < 2)
return 0;
if (n == 2)
return 1;
if (n == 3)
return 2;
int to3 = n / 3;
if (n - to3 * 3 == 1)
to3--;
int to2 = (n - to3 * 3) / 2;
return (int) (Math.pow(3, to3)) * (int) (Math.pow(2, to2));
}
}