剑指offer刷题笔记—剪绳子


一次搞定所有的剪绳子问题

剑指 Offer 14- I. 剪绳子I

原题地址:剪绳子I

题目描述:
给你一根长度为 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。

示例1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

思路:

首先规定一条绳子每次剪完后新产生的两截绳子称为子绳。

从简单的长度出发:

当绳子长度为2时,只能从长度为1的地方剪一次,子绳长度最大乘积为1。

当绳子长度为3时,可以在长度为1的地方剪一次,也可以在长度为2的地方剪一次,子绳长度最大乘积为2。

当绳子长度为4时,可以在长度为1的地方剪一次,可以在长度为2的地方剪一次,可以在长度为3的地方剪一次,子绳长度最大乘积为4。

当绳子长度为5时,可以在长度为1的地方剪一次,也可以在长度为2的地方剪一次,可以在长度为3的地方剪一次,可以在长度为4的地方剪一次,子绳长度最大乘积为6。

观察,可知假设当前绳子长度是n,可能下刀的地方有1,2,3,…,n-1,共n-1种可能。如果设在i处下刀,那么共产生两截子绳,长度分别为i和n-i(0 < i < n)。

接下来考虑计算最大乘积:

由题意可知:必须要剪一刀,那么初始的子绳长度乘积为i * (n-i),其中i为从1到n-1的数

分别对于长度为i和n-i的子绳,讨论在其上再剪是否会扩大子绳乘积。

对于长度为i的子绳,如果不再剪,子绳乘积为i,如果再剪,子绳乘积为dp[i],那么最大子绳乘积为 m a x ( i , d p [ i ] ) max(i, dp[i]) max(i,dp[i])

对于长度为n-i的子绳,如果不再剪,子绳乘积为n-i,如果再剪,子绳乘积为dp[n-i],那么最大子绳乘积为 m a x ( n − i , d p [ n − i ] ) max(n-i, dp[n-i]) max(ni,dp[ni])

注意,n并非一定是初始情况下的长度,也能代表着中间产生的任意一条子绳的长度,即n的大小与上一步操作有关。由此,该问题其实可以看作为一个动态规划问题。

1.dp数组的构建

首先进行dp数组的构建,数组中的数字应该代表着当前绳子总长度下的所有子绳长度的最大乘积。

2.状态转移方程

d p [ i ] = m a x ( d p [ i ] , m a x ( j , d p [ j ] ) ∗ m a x ( i − j , d p [ i − j ] ) ) dp[i] = max(dp[i], max(j, dp[j]) * max(i - j, dp[i - j])) dp[i]=max(dp[i],max(j,dp[j])max(ij,dp[ij]))

代码如下:

class Solution:
    def cuttingRope(self, n: int) -> int:
        dp = [0] * (n + 1)
        for i in range(2, n+1):
            for j in range(1, i):
                dp[i] = max(dp[i], max(dp[j], j) * max(dp[i-j], i-j))
        return dp[n]

剑指 Offer 14- II. 剪绳子 II

原题地址:剪绳子II

题目描述:

给你一根长度为 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。

答案需要取模 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

思路:

与第一问相比,问题的难度提升主要在绳子长度n的取值范围从58扩展到了1000,也就意味着之前使用的动态规划的方法,将会在空间复杂度和时间复杂度上受到限制。在这里,我们在不讨论繁琐的数学证明过程下,探究这道题如何从数学的角度出发寻找最优解。

当绳子长度为5时,子绳最大乘积为3*2。

当绳子长度为6时,子绳最大乘积为3*3。

当绳子长度为7时,子绳最大乘积为3*4。

当绳子长度为8时,子绳最大乘积为3*3*2。

当绳子长度为9时,子绳最大乘积为3*3*3。

当绳子长度为10时,子绳最大乘积为3*3*4。

因此,可以看出,子绳想要取得最大乘积就要取出尽可能多的3,再根据余数做如下计算:

(1)余数为0,子绳最大乘积为3 ** a,a为n // 3

(2)余数为1,子绳最大乘积为3 ** (a - 1) * 4,a为n // 3

(3)余数为2,子绳最大乘积为3 ** a * 2,a为n // 3

代码如下:

class Solution:
    def cuttingRope(self, n: int) -> int:
        l = [1, 2]
        if n < 4:
            return l[n - 2]
        a, b = n // 3, n % 3
        if b == 0:
            return 3 ** a % 1000000007
        if b == 1:
            return (3 ** (a - 1))* 4 % 1000000007
        if b == 2:
            return (3 ** a) * 2 % 1000000007

完结撒花~~~~~~~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值