剪绳子
给你一根长度为 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。
提示:2 <= n <= 58
分析
使用动态规划。
参考 https://leetcode.cn/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/1391865
-
状态定义:dp[i]为长度为i的绳子剪成m段最大乘积为dp[i]
-
状态转移:dp[i]有两种途径可以转移得到 2.1 剪成两段。前面单独成一段,后面剩下的单独成一段,乘积为j(i-j),乘积个数为2 2.2 大于两段。第一次剪去了j,剩余的就是dp[i-j],乘积dp[i-j]j
两种情况中取大的值作为dp[i]的值,同时应该遍历所有j,j∈[2,i-2],j从2开始,j=1做乘积没意义
-
初始化:初始化dp[1]=1即可
-
遍历顺序:显然为正序遍历
-
返回坐标:返回dp[n]
class Solution {
public int cuttingRope(int n) {
//因为dp下表从0开始,dp[0]是第一个,dp[n]是n+1个
int dp[] = new int[n+1];
//2 <= n <= 58,不管dp[1]
//m段,m>1,1x1=1
dp[2] = 1;
//i表示绳子长度,从3开始,小于3不用算了乘积都是1
for (int i=3; i<=n; i++) {
//j表示第一次剪去的长度,剩余i-j
//小于等于 i-2 是因为最后是 j*1 等于 j,没意义
for (int j=1; j<=i-2; j++) {
//两段直接相乘,或者i-j继续分段,比较取最大值
//int tmp = Math.max(dp[i-j]*j, (i-j)*j);
int tmp = Math.max(dp[i-j], (i-j)) * j;
//和前一个dp比较取最大值
dp[i] = Math.max(tmp, dp[i]);
}
}
return dp[n];
}
}
或者使用深度递归。
class Solution {
int maxRes = 1;
public int cuttingRope(int n) {
dfs(1, 1, n);
return maxnum;
}
/**
* @param start 每次剪去长度
* @param res 乘积结果
* @param n 剩余长度
*/
void dfs(int start, int res, int n) {
//不再剪了或者剩下1,得到乘积结果,对比取最大
if (n==0 || n==1) maxRes = Math.max(maxRes, res);
for (int i=0; i<n-1; i++) {
dfs(start+i, res, n-start-i);
}
}
}