Leetcode——剪绳子
Posted Yawn,
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode——剪绳子相关的知识,希望对你有一定的参考价值。
1. 题目
2. 题解
(1)动态规划
- 我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来
- 用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
- 我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪
- 当 i≥2 时,假设对正整数 i 拆分出的第一个正整数是 j(1≤j<i),则有以下两种方案:
将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j×(i−j);
将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j×dp[i−j]。 - 第一段长度j可以取的区间为[2,i),对所有j不同的情况取最大值,因此最终dp[i]的转移方程为
dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j])) - 最后返回dp[n]即可
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++){
//为什么可以固定一个j呢,因为j在遍历中都能被取到
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];
}
}
如果看不懂的话,来个最容易懂的:
- 固定划分点,继续看子划分区间能不能继续划分
class Solution {
public int cuttingRope(int n) {
int[] dp = new int[n + 1];
//i表示绳子长度
for(int i = 2;i <= n;i++){
//j表示分的段的长度,小于i
for(int j = 1;j < i;j++){
//都不能分了
int a = j * (i - j);
//j不能分,i - j能分
int b = j * dp[i - j];
//以此类推
int c = dp[j] * (i - j);
int d = dp[j] * dp[i - j];
int e = dp[i];
dp[i] = Math.max(Math.max(Math.max(a,b),Math.max(c,d)),e);
}
}
return dp[n];
}
}
(2)贪心
核心思路是:尽可能把绳子分成长度为3的小段,这样乘积最大
步骤如下:
- 如果 n == 2,返回1
- 如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
- 如果 n == 4,返回4
- 如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段
class Solution {
public int cuttingRope(int n) {
if(n == 2)
return 1;
if(n == 3)
return 2;
int res = 1;
while(n > 4){
res *= 3;
n -= 3;
}
return res * n;
}
}
当 n 很大时:
class Solution {
public int cuttingRope(int n) {
if(n == 2)
return 1;
if(n == 3)
return 2;
long a = 1;
long mod = 1000000007;
while(n > 4){
n = n - 3;
a = (a * 3) % mod;
}
long ans = a * n % mod;
return (int)ans;
}
}
以上是关于Leetcode——剪绳子的主要内容,如果未能解决你的问题,请参考以下文章