剑指 Offer 14- I. 剪绳子
题目描述
剑指Offer.66.剪绳子
给你一根长度为 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。
II:上面题目一样,但是答案需要取模1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
解题思路(动态规划)
比如在拆分3的时候,我们可以先拆分为1和2,得到一个乘积,然后把3分为1,1,1又得到一个乘积,我们再比较这两个乘积就好了
如果n=4,第一次切绳子可以满足的切法有(1,3)(2,2),我们后续的切法肯定是基于这两种切法中的较大值进行切分,很明显(2,2)更大,那我们还需要切吗?如果要切,只能对长度为2的绳子进行切割,然而2切了的话,最大乘积是1×1=1,何必呢?,还不如不切,因为2×2>1×1×2,那么(2,2)不再切呢,得到的乘积是4,所以n=4,最大乘积为4
现在如果n=5,
第一刀可以切成(1,4),(2,3),现在就变成了长度为1和4的绳子能切出来的乘积与长度为2和3的绳子能切出来的乘积作比较,看谁大,我们上面分析过,长度为4的绳子切出来最大乘积为4,那么(1,4)的最大值应该是1×4=4;
(2,3)的最大值为2×3=6;
所以,n=5,最大乘积为6;
我们求DP[5]切第一刀是不是可以先比较这两种切法,看哪个大,接下来,再求DP[2]和DP[3]这就是分成子问题
现在看n=6;有3种组合(1,5)(2,4)(3,3)
(1,5)可以对5进行切割,我们已经知道对5进行切割最大值是6,那么第一次切成(1,5)这种方案的最大值是1×6=6;
(2,4)可以对2和4进行切割,我们已经知道对4进行切割最大值是4,那么第一次切成(2,4)这种方案的最大值是2×4=8;
(3,3)可以对3和3进行切割,我们已经知道对3进行切割最大值是3(不切割),那么第一次切成(3,3)这种方案的最大值是3×3=9;
所以n=6,最大乘积为9;
我自己的写法(动态规划)
class Solution {
public int cuttingRope(int n) {
if(n==1)
return 0;
if(n==2)
return 1;
if(n==3)
return 2;
int dp[]=new int[n+1];//因为最后一次要用到dp[n]
// dp[0]=0;//默认为0,不用写
dp[1]=1;
dp[2]=2;
dp[3]=3;//n都大于等于4了,如果切出来有一块长度为3,那这个3就不用切了啊(3>2),上面n=3返回2是因为这段绳子必须切断
int max=0;
int val=1;
for(int i=4;i<=n;i++)
{
//max=0;
for(int j=1;j<=i/2;j++)
{
//j<=i/2是因为我们每次取两个值来做乘法,比如i=4,就只用算j=1,j=2的情况,因为j=3的话 val=dp[3]*dp[1],j=1时,val=dp[1]*dp[3],这就是一样的啊
//max=Math.Max()
val=dp[j]*dp[i-j];
max=max>val?max:val;//max取max和val中的最大值
}
dp[i]=max;//算出来了每一个i对应最大值,记得更新dp[i]的值啊,这样下次程序好直接取
}
return max;
}
}
视频中的解法(更精简)
class Solution {
public int cuttingRope(int n) {
if(n==1)
return 0;
if(n==2)
return 1;
if(n==3)
return 2;
int dp[]=new int[n+1];
// dp[0]=0;//默认为0,不用写
dp[1]=1;
dp[2]=2;
dp[3]=3;//n都大于等于4了,如果切出来有一块长度为3,那这个3就不用切了啊(3>2),上面n=3返回2是因为这段绳子必须切断
for(int i=4;i<=n;i++)
{
for(int j=1;j<=i/2;j++)
{
//j<=i/2是因为我们每次取两个值来做乘法,比如i=4,就只用算j=1,j=2的情况,因为j=3的话 val=dp[3]*dp[1],j=1时,val=dp[1]*dp[3],这就是一样的啊
dp[i]= Math.max(dp[i],dp[j]*dp[i-j]);
}
}
return dp[n];
}
}
这个内存消耗是非常扯淡的,每次运行不一样,35.4M只能超过18%
贪心算法
首先有两个推论,
① 当所有绳段长度相等时,乘积最大。
② 最优的绳段长度为 3 。
参考https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/
class Solution {
public int cuttingRope(int n) {
if(n <= 3) 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;//(剩了个1,就让它和3结合起来组成4)
return (int)Math.pow(3, a) * 2;//2*3>5哦,所以不结合
}
}
public int cuttingRope(int n) {
if (n == 2 || n == 3)
return n - 1;
int res = 1;
while (n > 4) {
//如果n大于4,我们不停的让他减去3
n = n - 3;
//计算每段的乘积
res = res * 3;
}
return n * res;
}
作者:sdwwld
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/shu-xue-zhi-shi-he-dong-tai-gui-hua-liang-chong-fa/
public int cuttingRope(int n) {
if (n == 2 || n == 3)
return n - 1;
else if (n % 3 == 0) {
//如果n是3的倍数,绳子全部剪为3
return (int) Math.pow(3, n / 3);
} else if (n % 3 == 1) {
//如果n对3求余等于1,我们剪出一个长度为4的,其他长度都是3
return 4 * (int) Math.pow(3, (n - 4) / 3);
} else {
//如果n对3求余等于2,我们剪出一个长度为2的,其他长度都是3
return 2 * (int) Math.pow(3, n / 3);
}
}
作者:sdwwld
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/shu-xue-zhi-shi-he-dong-tai-gui-hua-liang-chong-fa/
剑指 Offer 14- II. 剪绳子 II
class Solution {
public int cuttingRope(int n) {
if(n == 2) {
return 1;
}
if(n == 3){
return 2;
}
int mod = (int)1e9 + 7;
long res = 1;
while(n > 4) {
res *= 3;
res %= mod;
n -= 3;
}
return (int)(res * n % mod);
}
}
作者:HenryLee4
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/javatan-xin-si-lu-jiang-jie-by-henrylee4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
int b = n % 3, p = 1000000007;
long rem = 1, x = 3;
for(int a = n / 3 - 1; a > 0; a /= 2) {
if(a % 2 == 1) rem = (rem * x) % p;
x = (x * x) % p;
}
if(b == 0) return (int)(rem * 3 % p);
if(b == 1) return (int)(rem * 4 % p);
return (int)(rem * 6 % p);
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
long res=1L;
int p=(int)1e9+7;
//贪心算法,优先切三,其次切二
while(n>4){
res=res*3%p;
n-=3;
}
//出来循环只有三种情况,分别是n=2、3、4
return (int)(res*n%p);
}
}