代码随想录一刷之动态规划篇(一刷完结)

新年初五,刷题也懈怠了不少,今晚总算是刷完了代码随想录的所有章节(除了额外题目)
在做贪心题目有看到动态规划的解法但是并不理解,我也以为会很难理解,但真的去了解后才发现,有些题目确实不好理解,但画画图多花点时间还是能懂个七七八八

随笔

int n=3;
while(n–)执行3次

int n=3;
while(–n)执行2次

背包问题

dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,(这里的容量j为背包还能够容纳的物品的重量)价值总和最大是多少

若物品i放不进去/不放进去:由dp[i-1][j-0]推出

若物品i放得进去,由dp[i-1][j-weight[i]]+value[i]推出

dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值

分割等和子集

dp[j] == j 说明,集合中的子集总和正好可以凑成总和j

dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]

相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]

目标和

确定nums[i]),凑成dp[j]就有dp[j - nums[i]] 种方法

凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来

dp[j] += dp[j - nums[i]]

一和零

dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]

dp[i][j]=max(dp[i][j],dp[i-zeroNum][j-onrNum]+1);

完全背包

每件物品都有无限个(也就是可以放入背包多次)

第i件物品的重量是weight[i],得到的价值是value[i]

01背包中二维dp数组的两个for遍历的先后循序可以颠倒,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量

在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序是无所谓的!

01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次

完全背包的物品是可以添加多次的,所以要从小到大去遍历

组合不强调元素之间的顺序,排列强调元素之间的顺序

零钱兑换||

dp[j]:凑成总金额j的货币组合数为dp[j]

dp[j]+=dp[j-coins[i]];

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

组合总和 Ⅳ(实际是排列问题)

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1,0);
        dp[0]=1;
        for(int j=0;j<=target;j++){
            for(int i=0;i<nums.size();i++){//不能写j=nums[i],因为这里的i还没有初始化
                if(j-nums[i]>=0&&dp[j]<INT_MAX-dp[j-nums[i]]){
                    //C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
                    dp[j]+=dp[j-nums[i]];
                }
            }
        }
        return dp[target];
    }
};

零钱兑换

求凑成总金额所需的最少的硬币个数

先物品或先背包都可以

完全平方数

dp[j]:和为j的完全平方数的最少数量为dp[j]

先物品或先背包都可以

单词拆分

可以aab为例子,a,aa为子字符串

class Solution {
private:
    bool backtracking(const string&s,
            const unordered_set<string>& wordSet,
            vector<bool>& memory,
            int startIndex){
        if(startIndex>=s.size()){
            return true;
        }
        if(!memory[startIndex])return memory[startIndex];
        for(int i=startIndex;i<s.size();i++){
            string word =s.substr(startIndex,i-startIndex+1);
            if(wordSet.find(word)!=wordSet.end()&&backtracking(s,wordSet,memory, i+1))return true;
        }
        memory[startIndex]=false;
        return false;
    }

public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(),wordDict.end());//数值即字符串不可以重复
        vector<bool> memory(s.size(),1);
        return backtracking(s,wordSet,memory, 0);
    }
};

dp[i]:长度为i,为true时可拆分为字典的字符串

本题强调物品之间的顺序,所以使用排列,先遍历背包,再遍历物品

强调组合的话先遍历物品再遍历背包

多重背包

等价于多个01背包
打家劫舍——dp[i]偷到第i家所能偷到的最大金额

dp[i]=max(dp[i-2]+nums[i],dp[i-1]);

打家劫舍II——dp[i]偷到第i家所能偷到的最大金额

考虑去首与去尾两种情况

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==0)return 0;
        if(nums.size()==1)return nums[0];
        if(nums.size()==2)return max(nums[0],nums[1]);
        int result1=robRange(nums,0,nums.size()-2);
        int result2=robRange(nums,1,nums.size()-1);
        return max(result1,result2);
    }
    int robRange(vector<int>& nums,int start,int end){
        //if(start==end)return nums[start];
        vector<int> dp(nums.size());
        dp[start]=nums[start];
        dp[start+1]=max(nums[start],nums[start + 1]);
        for(int i=start + 2;i<=end;i++){
            dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[end];
    }
};

打家劫舍 III——1.递归重复计算超时

class Solution {
public:
    unordered_map<TreeNode*,int>umap;
    int rob(TreeNode* root) {
        if(root==NULL)return 0;
        if(root->left==NULL&&root->right==NULL)return root->val;
        int val1=root->val;
        if(root->left)val1+=rob(root->left->left)+rob(root->left->right);
        if(root->right)val1+=rob(root->right->left)+rob(root->right->right);
        int val2=rob(root->left)+rob(root->right);
        return max(val1,val2);
    }
};

2.记忆化递推

class Solution {
public:
    unordered_map<TreeNode*,int>umap;
    int rob(TreeNode* root) {
        if(root==NULL)return 0;
        if(root->left==NULL&&root->right==NULL)return root->val;
        if(umap[root])return umap[root];
        int val1=root->val;
        if(root->left)val1+=rob(root->left->left)+rob(root->left->right);
        if(root->right)val1+=rob(root->right->left)+rob(root->right->right);
        int val2=rob(root->left)+rob(root->right);
        umap[root]=max(val1,val2);
        return max(val1,val2);
    }
};

3.dp数组:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int rob(TreeNode* root) {//二维数组,不取该点与取该点取得的最大金钱
        vector<int> result=robTree(root);
        return max(result[0],result[1]);
    }
    vector<int> robTree(TreeNode* cur){
        if(cur==NULL)return vector<int>{0,0};
        vector<int> left=robTree(cur->left);
        vector<int> right=robTree(cur->right);
        int val1=max(left[0],left[1])+max(right[0],right[1]);//不偷cur点,那么可以偷也可以不偷左右节点
        int val2=cur->val+left[0]+right[0];//偷cur点
        return {val1,val2};
    }
};

买卖股票的最佳时机

dp[i][0] 表示第i天持有股票所得最多现金

dp[i][1] 表示第i天不持有股票所得最多现金

买卖股票的最佳时机III和IV

dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]

  1. 表示不操作
  2. 第一次持有股票
  3. 第一次不持有股票
  4. 第二次持有股票
  5. 第二次不持有股票

最佳买卖股票时机含冷冻期

dp[i][j],第i天状态为j,所剩的最多现金为dp[i][j]。

  • 状态一:持有股票状态(今天买入股票,或者是之前就买入了股票然后没有操作,一直持有)
  • 不持有股票状态,这里就有两种卖出股票状态
    • 状态二:保持卖出股票的状态(两天前就卖出了股票,度过一天冷冻期。或者是前一天就是卖出股票状态,一直没操作)
    • 状态三:今天卖出股票
  • 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!

买卖股票的最佳时机II

dp[i][0] 表示第i天不持有股票所得最多现金

dp[i][1] 表示第i天持有股票所得最多现金

最长连续递增序列

dp[i]:以下标i为结尾的连续递增的子序列长度为dp[i]

单调栈主要学了栈的另一种运用

致此一刷完结,要进入基础四大件的学习啦,当然还有二刷随想录
有点小问题是,课题还没有开始做头痛hhh

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值