对于本题,裁剪一段绳子,可以用递归方法进行求解,但是在用递归的时候会产生一系列的重复解,
例如计算f(6),可以分为f(2), f(4);计算法f(8),可以分为f(4), f(4);就产生了重复计算f(4); 递归是自顶向下;我们在本题中可以利用自底向上的计算(动态规划),减少重复计算,同时可以利用贪心算法快速计算。
方法一:动态规划
从题意n > 1, m > 1;因此n最小为2,并且根据题意,最少切一次,因此2切成1和1;即在n=2时,返回1;动态规划采用自底向上,因此我们需要设置初始值dp[0] = 0, dp[1] = 1, dp[2] = 1; 然后从n=3开始计算,一直到n;在计算过程中,我们用两层for循环来实现裁剪的过程;
转移方程是max(dp[i], max(j*(i-j), j * dp[i - j]))
代码如下:
class Solution:
def cuttingRope(self, n):
if n == 2:
return 1
dp = [0] * (n+1)
dp[0] = 0
dp[1] = 1
dp[2] = 1
for i in range(3,(n + 1)):
# max1 = 0
for j in range(1,(i//2)+1):
dp[i] = max(dp[i], max(j*(i-j), j * dp[i - j]))
return dp[n]
从上述代码中可以看出我们采用两个for循环,
外层for循环实现绳子长度从3到n时最大乘积的计算;
内层for循环实现绳子i裁剪的过程,求出绳子i裁剪的乘积最大值;其中j的范围为1,(i//2) + 1; 原因是如果长度为5,那么裁剪为2和3与3和2是相同的效果;
状态转移方程的含义:
max(dp[i], max(j*(i-j), j * dp[i - j]))
我们要取出绳长为i裁剪后的最大乘积,其中最大乘积初始化为0,j从1开始变化,即我们裁剪一刀,第一刀的变化过程;然后i变为j和i-j,接下来我们需要考虑以下情况,即绳子是否要继续裁剪;因为我们裁剪成为了两段,所以有四种可能;
根据分析,我们可以剔除j接着裁剪的两种情况(2,3),只剩余第一项和第四项。原因如下:
例如i为11的情况:
j为5时,i-j为6;
其中j从1变化到5;
当j为3时,i-j为8
3 8
3不再切割
8可以切割的结果如下:
1,4
2,6
3,5
4,4
我们看3,2,6这一组数据;
2不再切割,对6进行切割;
即最大乘积就变成了 32dp[6]
这正是j为5,i-j为6时的子过程;
因此,由于j时从1开始变化的,所以j变大后的值的切割结果保存在小于j的过程中,因此,j不再进行切割。
所以状态转移方程如下所示:
max(dp[i], max(j*(i-j), j * dp[i - j]))
方法二:贪心算法:
参考这个大神的做法:
https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/
思想是裁剪得到的3越多,乘积越大。
如果是除以3余1,则最后一个是4,切割为2*2;
如果是除以3余2,则最后一个是2.
代码如下:
class Solution:
def cuttingRope(self, n):
if n == 2:
return 1
if n == 3:
return 2
if n % 3 == 0:
return 3**(n//3)
if n % 3 == 1:
return 3**(n//3 - 1) * 4
if n % 3 == 2:
return 3**(n//3) * 2
另附动态规划的四种情况代码:
转移方程为:
max(dp[i], max(j*(i-j), dp[j] * (i-j), j * dp[i - j], dp[j] * dp[i - j]))
class Solution:
def cuttingRope(self, n):
if n == 1:
return 1
if n == 2:
return 1
dp = [0] * (n+1)
dp[0] = 0
dp[1] = 1
dp[2] = 1
for i in range(3,(n + 1)):
# max1 = 0
for j in range(1,(i//2)+1):
dp[i] = max(dp[i], max(j*(i-j), dp[j] * (i-j), j * dp[i - j], dp[j] * dp[i - j]))
return dp[n]