算法训练营(day41)

动态规划理论基础

动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。

举个例子:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

动态规划中 dp[j] 是由 dp[j-weight[i]] 推导出来的,然后取 max(dp[j], dp[j - weight[i]] + value[i])

动态规划的解题步骤

  1. 确定 dp 数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp 数组如何初始化
  4. 确定遍历顺序
  5. 举例推导 dp 数组

343. 整数拆分

题目链接:https://leetcode.cn/problems/integer-break/description/

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

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

解题思路

解题过程:动态规划

按照动规五部曲来分析:

  1. 确定dp数组(dp table)以及下标的含义

    • dp[i]:分拆数字i,可以得到的最大乘积
  2. 确定递推公式

    dp[i]由两种方式获得:

    • j * (i - j):单独分成两份进行相乘
    • j * dp[i - j]:分成两份后,再对 i-j这一份进行拆分
  3. dp数组的初始化

0和1是没有意义的,因为不能拆分,所以dpdp[2] = 1开始

  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
2
3
4
5
for(int i = 3; i <= n; i++){
for(int j = 1; j <= i / 2; j++){
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
}
}
  1. 推导dp数组

详细代码

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n + 1];
dp[2] = 1;
for(int i = 3; i <= n; i++){
for(int j = 1; j <= i / 2; j++){
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];
}
}

96. 不同的二叉搜索树

题目链接:https://leetcode.cn/problems/unique-binary-search-trees/description/

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

解题思路

解题过程:动态规划

按照动规五部曲来分析:

  1. 确定dp数组(dp table)以及下标的含义

    dp[i]:表示从1到i 的节点作为根节点时的排列种数

  2. 确定递推公式

    dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]

​ 化简为:dp[i] += dp[j - 1] * dp[i - j]

  1. dp数组的初始化

dp[0] = dp[1] = 1

  1. 确定遍历顺序

​ 遍历节点数,从递归公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为 i 的状态是依靠 i之前节点数的状态

​ 那么遍历 i 里面每一个数作为头结点的状态,用 j 来遍历

  1. 推导dp数组

详细代码

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int numTrees(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; i++){
for(int j = 1; j <= i; j++){
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
}