前言
更详细的在大佬的代码随想录 (programmercarl.com)
本系列仅是简洁版笔记,为了之后方便观看
不同的二叉搜索树
96. 不同的二叉搜索树 - 力扣(LeetCode)
通过举例子发现重叠子问题
代码很简单,主要是思路问题,知道二叉搜索树的概念,并分别对左右子树进行计算,相乘
class Solution {
public:int numTrees(int n) {vector<int>dp(n+1);dp[0]=1;for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++){dp[i]+=dp[j-1]*dp[i-j];}}return dp[n];}
};
01背包
二维01背包
dp[i][j]表示 [0-i]的物品里任意取容量为j的背包的价值
- 不放物品i:dp[i][j]=dp[i - 1][j]
- 放物品i:dp[i][j]=dp[i - 1][j - weight[i]] + value[i]
- 注意:非零下标不管初始化什么值,都不影响最后结果,但是有零下表初始化需要在注意
dp[0][j],当 j < weight[0]的时候,放不进去,dp[0][j] = 0;当j >= weight[0]时,dp[0][j] =value[0]
vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0)); for (int j = weight[0]; j <= bagweight; j++) {dp[0][j] = value[0];}
二维数组实现的dp01背包for循环可以颠倒
for(int i = 1; i < weight.size(); i++) { // 物品for(int j = 0; j <= bagweight; j++) { // 背包if (j < weight[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}}
一维01背包
dp[j]容量为j的背包的价值
- 不放物品i:dp[j]=dp[j]
- 放物品i:dp[j]=dp[j - weight[i]] + value[i]
- 注意:非零下标不管初始化什么值,都不影响最后结果,但是有零下标初始化为非负数的最小值0就可以
vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0)); for (int j = weight[0]; j <= bagweight; j++) {dp[0][j] = value[0];}
一维数组实现的dp01背包for循环不可以颠倒,背包必须倒序输出,这样才能符合每个背包只能使用一次
vector<int> dp(N + 1, 0);for (int i = 0; i < 物品数量; ++i) {for (int j = N; j >= costs[i]; --j) {dp[j] = max(dp[j], dp[j - costs[i]] + values[i]);}}
分割等和子集
416. 分割等和子集 - 力扣(LeetCode)
把数组分割成两个元素和相等的子集,可以弄成01背包问题,每个数只能使用一次,观察是否能把num/2的背包容量给填满
注意:本题重量和价值是统一变量
dp[j] == j 时集合中的子集总和正好可以凑成总和j
class Solution {
public:bool canPartition(vector<int>& nums) {int sum=0;vector<int>dp(10001,0);for(int i=0;i<nums.size();i++){sum+=nums[i];}if(sum%2==1) return false;//说明凑不成两个一样的数int target=sum/2;for(int i=0;i<nums.size();i++){for(int j = target; j >= nums[i]; j--) {dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);} }if (dp[target] == target) return true;return false; }
};
最后一块石头的重量II
1049. 最后一块石头的重量 II - 力扣(LeetCode)
两两石头相撞,最终取得最小差值,可以分成两个数量之和相近的堆,来进行计算是上一题的演变,重量和价值是统一变量
target = sum / 2向下取整,所以sum - dp[target] >=dp[target],,所以最终结果就是用大的减去小的
class Solution {
public:int lastStoneWeightII(vector<int>& nums) {int sum=0;vector<int>dp(15001,0);for(int i=0;i<nums.size();i++){sum+=nums[i];}int target=sum/2;for(int i=0;i<nums.size();i++){for(int j = target; j >= nums[i]; j--) {dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);} }return sum - dp[target] - dp[target]; }
};
目标和
494. 目标和 - 力扣(LeetCode)
一个集合分出两个子集,加法left集合和减法right集合
left- right = target。
left + right = sum
right = sum - left
left - (sum - left) = target
left = (target + sum)/2
targe,sum是固定的,所以就可以转化为在集合nums中找出和为left的组合
class targetolution {
public:int findTargettargetumWays(vector<int>& nums, int target) {int sum = 0;for (int i = 0; i < nums.size(); i++) sum += nums[i];if (abs(target) > sum) return 0; if ((target + sum) % 2 == 1) return 0; int bagtargetize = (target + sum) / 2;vector<int> dp(bagtargetize + 1, 0);dp[0] = 1;for (int i = 0; i < nums.size(); i++) {for (int j = bagtargetize; j >= nums[i]; j--) {dp[j] += dp[j - nums[i]];}}return dp[bagtargetize];}
};
一和零
474. 一和零 - 力扣(LeetCode)
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
for (int i = m; i >= zeroNum; i--) { for (int j = n; j >= oneNum; j--) {dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);}
}