offer 第14-I题 剪绳子(medium)

offer 第14-I题 剪绳子(medium),第1张

offer 第14-I题 剪绳子(medium) 前言

该系列文章为本人刷leetcode的记录,主要旨在分享刷题的思路及算法解析(尽可能的一题多解),另方便自己日后查阅回顾。代码的实现语言是python和go。

想进大厂免不了刷题,一起加油吧,小伙伴们!

题目 offer 第14-I题 剪绳子(medium)

链接: https://leetcode-cn.com/problems/jian-sheng-zi-lcof/

题目内容

给你一根长度为 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 <= n <= 58
解题思路

分析:该题具有明显的动态规划公式(迭代解法);递归解法(会超时);

解法1:尾递归或者记忆递归

解法2: 动态规划

假设n为绳长;dp[n]表示n长度的绳子剪成m段后的最大长度;

其中如果长度为n的绳子剪完i后不再继续剪了,则有dp[n]=i*(n-i);

如果剪完后还继续剪(n-i)这一段绳的话,则有dp[n]=i*dp[n-i];

所以dp[n]=max(dp[n],i*(n-i), i *dp[n-i]);因为dp[n]一开始就可能不剪的时候已经最大了;

代码实现 python
def cuttingRope(n:int) -> int:

    #方法1:尾递归;这题有点像阶乘的思想
    #递归就是不断尝试;剪完第一刀后开始剪第2刀,依次类推;第一刀有n-1种剪法;
    #这题也像走迷宫那题;不过走迷宫那题走完第一个点后只能往上下左右四个点走;这题剪完第一刀i后,实际上下一步尝试的范围是(0,n-i)

    #
    max_val = 1
    # #传递的参数是剪完当前段还剩多少长度的绳子;以及剪完当前长度绳子后的乘积;
    def dfs(n0,mul):
        #尾递归终止条件
        nonlocal max_val

        #剪到最后一段绳子如果剩3及以下都不应该再剪了;此时已经是最大了
        if n0==0: max_val=max(max_val,mul)
        elif n0<=3: max_val = max(max_val,mul*n0)
        else:
            #除去中间和最后一段按理说正经的递归写法应该是如下;但是这样写会递归会超时;因为有大量相同的子问题被重复计算了;
            # for i in range(1,int(n/2)+1):
            #     dfs(n0-i,mul*i)
            # 解决方法1:记忆化递归;算出来子问题的解进行存储;遇到重复的就直接读取就行
            #解决方法2:尾递归;实际上在进行中间段的切割的时候,每次要么切割2或者切割3;其余切割其他数字的问题都可以转化为切割2或者3;
            #所以只用尝试切割2和3就行;
            #return dfs(n0-2,mul*2) or dfs(n0-3,mul*3)
            #实际上每一段切成3是结果是最大的;因为中间段的绳子一定是2或者3的倍数;假如恰好是2和3的公倍数;易证这时候都切成3肯定更大;
            #假如不是公倍数;比如中间段是2n;那么一定可以转换成m=3*n1+2;同理m=2*n2+3;故又可以变成公倍数问题;肯定取3更大;
            return dfs(n0-3,mul*3)



    # #剪第一刀;如果n=2时第一刀不能直接剪2;当n>3时候肯定是从2开始剪起更大;
    for i in range(1,int(n/2)+1):
        dfs(n-i,i)



    return max_val



    #方法2:动态规划;
    #该题的动态规划公式
    #dp[n] = dp[n-i]*i
    #如果n-i也不再继续剪了那么:dp[n]=i*(n-i)
    # 其中dp[0]=1;dp[1]=1;dp[2]=1;dp[3]=2

  #  动态规划的过程就是从最开始的一直迭代到最后的过程;
  #   dp=[1]*(n+1)
  #   dp[0]=1
  #   dp[1]=1
  #   dp[2]=2
  #   for i in range(3,n+1):
  #       for j  in range(1,i):
  #           #对绳长i来说如果剪一刀j,之后如果不剪了那么绳长就是(j)*(i-j),但是如果(i-j)这一段还要继续剪那么长度就是j*dp[i-j]
  #           dp[i] = max(dp[i-j]*j,j*(i-j),dp[i])
  #
  #
  #   return dp[n]

go
func max_int(n1,n2 int) int{
	if n1>=n2{return n1
	}else {return n2}
}

func max_int3(n1,n2,n3 int) int{
	//使用冒泡排序
	if n1>=n2{n1,n2=n2,n1}
	if n2>n3{n2,n3=n3,n2}
	return n3

}

func cuttingRope(n int) int{
	方法1:尾递归
	//max_val := 1
	//var recur func(n0,mul int) int
	//recur = func(n0,mul int) int{
	//	//递归终止条件
	//	if n0==0{ max_val= max_int(max_val,mul)
	//	}else if n0<=3{ max_val=max_int(max_val,mul*n0)
	//	}else{
	//		return recur(n0-3,mul*3)
	//
	//	}
	//	return -1
	//
	//
	//}
	//i:=0;
	//for ;i 
总结 

注意下递归会超时的问题;需要使用尾递归或者记忆化递归;

动态规划解法时候要考虑全面,不要漏掉其他可能最大值可能出现的情况;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5161155.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-18
下一篇 2022-11-18

发表评论

登录后才能评论

评论列表(0条)

保存