贪心算法和动态规划

引言

738. 单调递增的数字 - 力扣(LeetCode)

96. 不同的二叉搜索树 - 力扣(LeetCode)

416. 分割等和子集 - 力扣(LeetCode)

代码

第一题

这一题的思路就是我们从最后面开始向前遍历,找到第一个开始不符合单调递增的数字,然后把那个数据进行一个标记,同时-1,因为我们都知道,这个数后面的所有数都要变成9,因为可以保证递增的前提下数是最大的,但是如果这个数不-1,那么更新后的数一定比本数据要大。

cpp 复制代码
class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string strNum = to_string(n);
        int flag = strNum.size();
        for (int i = strNum.size() - 1; i > 0; i--) {
            if (strNum[i - 1] > strNum[i]) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for (int i = 0; i < strNum.size(); i++) {
            if (i >= flag) {
                strNum[i] = '9';
            }
        }
        return stoi(strNum);
    }
};

动态规划的思路

第一步:确定dp数组的含义,比如dpi表示的是第i个的。。。。

第二步:确定递推公式

第三步:初始化dp数组

第四步:确定遍历顺序

第二题

这一题我们先要明白dp的含义是当有n个结点的时候有多少种情况。我们要讨论二叉搜索树的情况,主要就是根节点的不同,不同的根节点就代表把一颗树分成了什么样,然后子树又继续分,所以我们可以很容易的想到,我们需要轮流遍历每个根节点,然后假设第j个是根节点的时候,左边又j - 1个结点,右边有i - j个结点,所以根据dp的含义,就有了递推公式dpi += dpj - 1 * dpi - j

cpp 复制代码
class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n + 1, 0); // n个结点的数量
        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背包

dpij的含义是我们处理从第一个物品到第i个物品,这个时候背包的承受量是j,此时最大的价值是多少。

二维数组

先遍历物品

cpp 复制代码
// 先遍历物品
for (int i = 1; i < weight.size(); i++) {
    for(int j = 0; j <= bagWeight.size(); j++) {
        if (weight[i] > j) {
            dp[i][j] = dp[i - 1][j];
        } else {
            dp[i][j] = max(dp[i][j - weight[i]] + value[i], dp[i - 1][j]);
        }
    }
}

先遍历背包容量

cpp 复制代码
// weight数组的大小 就是物品个数
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        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]);
    }
}

一维数组

cpp 复制代码
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}

第三题

这一题我们很难想到是用01背包来写的,我们用01背包来写的思路是因为数组的和可以分成两个部分,那么把和分成两半,target就是背包的容量。这里的dpj表示容量为j的背包最大价值是多少,因为这里重量和价值是一样的,所以我们只需要判断容量为target的时候价值最大是不是target,然后也不需要担心会不会有numsi比target还要大,因为我们有j >= numsi在兜底,可以保证我们每一次的背包都不会超重。

cpp 复制代码
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        if (sum % 2 != 0) {
            return false;
        } 
        int target = sum / 2;
        vector<int> dp(1000000, 0);
        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 (target == dp[target]) {
            return true;
        }
        return false;
    }
};

总结

本篇文章到这里就结束了!!!希望可以帮助大家理解~~~~

相关推荐
一只旭宝13 小时前
Python 与 C/C++ 内存模型对比总结
c语言·c++·python
心态还需努力呀13 小时前
把 Flameshot 编译进鸿蒙 PC:一次 Qt C++ 截图工具的源码级移植实战
c++·qt·harmonyos
shehuiyuelaiyuehao13 小时前
算法44,模拟算法,数青蛙
算法·哈希算法·散列表
hanlin0314 小时前
刷题笔记:力扣第287题-寻找重复数
笔记·算法·leetcode
kevin_kang14 小时前
第02章 对话时间线与延迟分析
算法
科学实验家14 小时前
最小生成树:Prim,kruskal
数据结构·c++·算法
学习智者14 小时前
《玄》IDE v3.6.3重磅发布:全功能修复与性能飞跃
开发语言·c++·ide·算法·中文语言 玄
Jasmine_llq14 小时前
《P10263 [GESP202403 八级] 公倍数问题》
算法·数论·快速 io 优化·埃氏筛(倍数枚举)·线性遍历求和
2402_8828938614 小时前
C++异常:从概念到实践的一篇博客
c++·异常
橘子汽水16814 小时前
Leetcode 322 279 零钱兑换,完全平方数
算法·leetcode