看了评论区大佬,第一次看见别人用数学公式解题,跟着算一下,为此专门跑去看了导数:
第一次用数学公式来写题:
推导公式解析:
利用结论进行代码实现,结论就是我们把这段绳子尽可能分成长度为3的小段,比如8,就分为3 3,最后还有个2分不了3了,那就分2.所以8分为 3 3 2,此时乘积为18,为8分的最大乘积值。
class Solution {
public:
int cuttingBamboo(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;
}
};
js:
/**
* @param {number} bamboo_len
* @return {number}
*/
var cuttingBamboo = function(n) {
if(n==2||n==3) return n-1;
var res=1;
while(n>4)
{
n-=3;
res*=3;
}
return n*res;
};
代码解析:
根据我们将才推导得到的结论:n/e e为3时乘积最大。所以我们应该尽可能把n分为长度为3的多个小段。
那么我们所有的n值都可以套用这个公式,but有几个例外:
n=1,当n为1时,没办法分第二段,因此无意义。不用计算
n=2,n=2只能拆分为1 1 ,乘积仍然为1,无其他分法,无其他乘积,因此不用计算。
n=3,n=3时可以拆分为1 1 1或 1 2,其中(1*2)<3 ,没有太大收益,。
而3不能够划分长度为3的多个小段,因此按照其他情况处理。
n=4,n=4时按照我们的推导思路,应该划分为1 3,此刻乘积为最大值。but我们发现其实并不然:2*2>(1*3)
所以4按照特殊情况处理。
除此之外,其他n值都可以按照尽可能划分为长度为3的多个小段这个推导结论进行。
验证:当 n=5时 划分:3 2 ,3*2=6为最大乘积。
然后我们发现规律:可以按照推导结论进行处理的全部大于4 ,不能按照推导结论处理的全小于4(包括4)。
因此我们可以用while循环:当n<4,按其他情况处理,n>4按推导结论处理。
首先解释这行: if (n == 2 || n == 3)
return n - 1;
n=2,我们可以拆分为1 1 此时1*1=1 也就是return n-1=2-1=1,结果对上了。
当n=3时,可以拆分为 1 1 1或 1 2,因为(1*1*1)<(1*2),所以应该拆分为1*2 ,return
n-1=3-1=2,结果对上了。
while(n>4) // 4是特殊的情况,4以后的数字都可以按推导结论来求最大乘积
{
n-=3;
res*=3;
}
这段的意思就是如果n>5,那么就让n-3,直到减到4为止停下。(-3的目的就是为了把n尽可能分为长度为3的多个小段)
每减一次就把乘一次3,当减到小于4之后就跳出 (*3的目的是为了求乘积分和)
此时再把一共的乘积再乘当前的n值,得到的就是我们要的最大的乘积 (一共的乘积分*当前n其实就是求n个3的乘积,n个3的乘积就是最大乘积)
举例说明,反推法,此刻我们已知当n=8时,拆分为 3 3 2,此刻3*3*2=18,为最大乘积值。
我们把n=8带入
var res=1;
while(8>4)
{
n-=3 -> n=5;
res=1*3=3;
}
while(5>4)
{
n-=3 -> n=2;
res=3*3=9;
}
while(2<4)
{
}
return res*n ->9*3 ->18
总结:每次切掉3,记录有几个三,剩下的长度继续如法炮制!2-4不参与切
第二种思路:dp
Leetcode(动态规划)(剑指Offer14-剪绳子)-Python-343-整数拆分_哔哩哔哩_bilibili
class Solution {
public:
int cuttingBamboo(int n)
{
vector<int> dp(n+1,0);
//int dp[]=new int(n+1);
if(n==3||n==2)return n-1;
for(int i=4;i<=n;i++)//
{ int MAX=0;
for(int j=1;j<i;j++)
{
MAX=max(MAX,max((i-j)*j,dp[i-j]*j));
}
dp[i]=MAX;
}
return dp[n];
}
};
解析
设i=4 i<=n i++
//i从4开始的原因是i=1,不可切不处理,i=2,i=3,乘积都小于本身,收益低,对它们单独处理:if(n==3||n==2)return n-1;
设j=1;j<=n/2 j++
//j<=n/2原因后面讲
i为外循环,j为内循环 i循1次,j循到n-1次
假设n=6 此时i=4 j=1
dp[i - j] * j = dp[3] * 1 = dp[2] * 1 = dp[1] * 1 = 1 * 1 = 1
[i-j]*j=3*1=3
3>1 return 3->MAX
j++ j=2
dp[i-j]*j=dp[4-2]*2=dp[2]*2=dp[1]*2=1*2=2
[i-j]*j=[4-2]*2=2*2=4
4>3 return 4
把4和上次MAX里存着的最大值3作比较,4>3 ,所以3丢弃,给MAX重复赋值为最大值4,即return 4->MAX
j++ j=3
dp[i-j]*j=dp[4-3]*2=dp[2]*2=dp[1]*2=1*2=2
[i-j]*j=[4-3]*2=1*2=2
2>=2 return 2 ->MAX
j++ j=4
dp[i-j]*j=dp[4-4]*4=0
[i-j]*j=[4-4]*4=0
等于0无意义,所以j=4不成立,j不能大于等于i,j应该<i-1或者j<i/2
循环结束,把MAX里存着的最大值给dp[i]存着,即return MAX(4)->dp[4];
回到外层循环,i++ i=5 j=1
dp[i-j]*j=dp[4]*1=dp[3]*1=dp[2]*1=dp[1]*1=1*1=1
[i-j]*j=4*1=4
4>3 return 4->MAX
j++ j=2
dp[i-j]*j=dp[4]*2=dp[3]*2=dp[2]*2=dp[1]*2=1*2=2
[i-j]*j=4*2=8
8>2 return 8->MAX
j++ j=3
dp[i-j]*j=dp[2]*3=dp[1]*3=3
[i-j]*j=2*3=6
6>3 取6,6再和之前MAX最大值8进行比较 8>6,舍弃6,return 8->MAX
j++ j=4
dp[i-j]*j=dp[1]*4=1*4=4
[i-j]*j=1*4=4
4>=4 再与MAX比较,8大,return 8->MAX
结束循环,此时return MAX(8)->dp[5]
回到外层循环 i++
i++ i=6
dp[i-j]*j=1
[i-j]*j=5*1=5
5>=1 return 5->MAX
j++ j=2
dp[i-j]*j=2
[i-j]*j=4*2=8
8>2 和上次最大值5进行比较,8>5 return 8->MAX
j++ j=3
dp[i-j]*j=3
[i-j]*j=3*3=9
9>3 和上一次最大值8比较,9>8,return 9->MAX
j++ j=4
dp[i-j]*j=4
[i-j]*j=2*8=8
8>4 8<9 return 9->MAX
j++ j=5
dp[i-j]*j=5
[i-j]*j=1*5=5
5>2 5<9 return 9->MAX
j=5 j=n/2, j结束
i=6 i=n i结束
结束循环,此时 return MAX(9)->dp[5]
结束,比较此时dp[n]的值
dp[]={0 1 1 2 4 8 9}
因为我们求的是n=6,所以找dp[6]
dp[6]=9,return 9
9就是最大乘积值