Leetcode刷题笔记41:动态规划3(343. 整数拆分-96. 不同的二叉搜索树)

96 阅读2分钟

导语

leetcode刷题笔记记录,本篇博客是贪心部分的第二期,主要记录题目包括:

Leetcode 343. 整数拆分

题目描述

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积 。

 

示例 1:

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

示例 2:

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

 

提示:

  • 2 <= n <= 58

解法

使用动规五部曲:

  1. dp[i]:代表了i拆分后得到的最大乘积;
  2. 递推公式:dp[i]=max(j(ij),jdp[ij])dp[i]=max(j*(i-j), j*dp[i-j]),这里j是所有小于i的数字循环,其中j*(i-j)是拆分为两个数的情形,j*dp[i-j]则是拆分为3个及以上的情形;
  3. 初始化:dp[2]=1,dp[0],dp[1]没有意义;
  4. 遍历顺序:从n等于3开始遍历;
  5. 打印输出。
class Solution:
    def integerBreak(self, n: int) -> int:
        # 初始化一个长度为 n+1 的数组,其中 dp[i] 表示数字 i 拆分成多个正整数之后,这些正整数的最大乘积。
        dp = [0] * (n+1)
        
        # 对于数字 2,只有一种拆分方式,即 1+1,所以乘积为1。
        dp[2] = 1

        # 从数字 3 开始,逐个计算其拆分后的最大乘积。
        for i in range(3, n+1):
            # 拆分数字 i,可以从 1 开始拆分到 i//2(因为超过 i//2 后,另一部分也会超过 i//2,产生重复)。
            for j in range(1, i//2 + 1):
                # 对于每种拆分方式,有两种选择:直接使用拆分出的两个数字 j 和 i-j;
                # 或者使用数字 j 和数字 i-j 拆分后得到的最大乘积 dp[i-j]。
                # 我们要选择两者中的较大值,并更新 dp[i]。
                dp[i] = max(j*(i-j), j*dp[i-j], dp[i])

        # 返回数字 n 拆分后得到的最大乘积。
        return dp[n]

Leetcode 96. 不同的二叉搜索树

题目描述

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

 

示例 1:

输入: n = 3
输出: 5

示例 2:

输入: n = 1
输出: 1

 

提示:

  • 1 <= n <= 19

解法

动规五部曲:

  1. dp[i]:表示由 i 个节点组成的独特BST的数量。
  2. 递推公式:观察n=3的情况,一共分为3种情况:
    • 头结点为1:左边0个节点,右边2个节点;
    • 头结点为2:左边1个节点,右边1个节点;
    • 头结点为3:左边2个节点,右边0个节点;

对于每一种情况,该种情况下的可能数量为左子树可能的情况乘以右子树可能的情况,然后循环处理每种情况,为此,最终的递推公式为:

dp[i]+=dp[j1]dp[ij]j=1,,idp[i] += dp[j-1] * dp[i-j] j=1,……,i
  1. 初始化,这里的关键在于dp[0],由于要计算乘积,所以可设置为dp[0]=1,这也符合直觉,即一个空节点也算是二叉搜索树,然后是dp[1]=1。
  2. 遍历顺序,从小往大;
  3. 打印dp数组。

image.png

class Solution:
    def numTrees(self, n: int) -> int:
        # 初始化一个长度为 n+1 的数组,dp[i] 表示由 i 个节点组成的独特BST的数量。
        dp = [0] * (n+1)
        
        # 基本情况:0个节点和1个节点的BST都只有1种。
        dp[0] = 1
        dp[1] = 1
        
        # 从2到n计算每一个数可以构成的BST数量。
        for i in range(2, n+1):
            # 对于每一个数字 j (1 到 i),考虑 j 作为根的BST。
            for j in range(1, i+1):
                # j-1 是左子树的节点数,i-j 是右子树的节点数。
                # dp[j-1] 是左子树的BST数量,dp[i-j] 是右子树的BST数量。
                # 二者的乘积即为 j 作为根的BST的数量。
                dp[i] += dp[j-1] * dp[i-j]
            
        # 返回数字 n 可以构成的BST数量。
        return dp[n]