算法训练营(day41)
算法训练营(day41)
动态规划理论基础
动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
举个例子:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
动态规划中 dp[j]
是由 dp[j-weight[i]]
推导出来的,然后取 max(dp[j], dp[j - weight[i]] + value[i])
。
动态规划的解题步骤
- 确定
dp
数组(dp table)以及下标的含义 - 确定递推公式
dp
数组如何初始化- 确定遍历顺序
- 举例推导
dp
数组
343. 整数拆分
题目链接:https://leetcode.cn/problems/integer-break/description/
给定一个正整数 n
,将其拆分为 k
个 正整数 的和( k >= 2
),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。
解题思路
解题过程:动态规划
按照动规五部曲来分析:
确定dp数组(dp table)以及下标的含义
dp[i]
:分拆数字i
,可以得到的最大乘积
确定递推公式
dp[i]
由两种方式获得:j * (i - j)
:单独分成两份进行相乘j * dp[i - j]
:分成两份后,再对i-j
这一份进行拆分
dp数组的初始化
0和1是没有意义的,因为不能拆分,所以dp
从 dp[2] = 1
开始
确定遍历顺序
根据递推公式,
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
,i 和 j 都是从左到右依次遍历的;dp[i]
是依靠dp[i - j]
的状态,所以遍历i
一定是从前向后遍历,先有dp[i - j]
再有dp[i]
1 | for(int i = 3; i <= n; i++){ |
- 推导dp数组
详细代码
1 | class Solution { |
96. 不同的二叉搜索树
题目链接:https://leetcode.cn/problems/unique-binary-search-trees/description/
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
解题思路
解题过程:动态规划
按照动规五部曲来分析:
确定dp数组(dp table)以及下标的含义
dp[i]
:表示从1到i
的节点作为根节点时的排列种数确定递推公式
dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
化简为:dp[i] += dp[j - 1] * dp[i - j]
- dp数组的初始化
dp[0] = dp[1] = 1
- 确定遍历顺序
遍历节点数,从递归公式:dp[i] += dp[j - 1] * dp[i - j]
可以看出,节点数为 i
的状态是依靠 i
之前节点数的状态。
那么遍历 i
里面每一个数作为头结点的状态,用 j
来遍历
- 推导dp数组
详细代码
1 | class Solution { |