当前位置: 首页 > article >正文

【Day 30 LeetCode】动态规划DP

一、动态规划DP

1、01背包

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大,这就是标准的背包问题。我们采用动态规划的思想去解决背包问题。
首先需要确定dp数组,有两个维度,物品和重量,dp[i][j]表示0~i的物品在最大容量为j的背包里,最大的价值总和
然后再确定递推公式,对于物品i,有放入背包和不放入两种选择,所以递推公式为 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e s [ i ] ) dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+values[i]) dp[i][j]=max(dp[i1][j],dp[i1][jweight[i]]+values[i])
初始化,j=0时任何物品下价值dp[i][0]都是0;i=0时,只有背包容量能装下物品0才能产生价值,dp[0][j]=0,j>=weight[0]。
代码如下:

# include<iostream>
# include<vector>

using namespace std;

int main(){
    int m, w; // m为物品数,w为背包容量
    cin >> m >> w;
    vector<int> weights(m); // 物品重量
    vector<int> values(m); // 物品价值
    for(int i=0; i<m; ++i)
        cin >> weights[i];
    for(int i=0; i<m; ++i)
        cin >> values[i];
    // 创建01背包的dp数组并初始化
    vector<vector<int>> dp(m, vector<int>(w+1));
    for(int i=weights[0]; i<=w; ++i)
        dp[0][i] = values[0];
    // dp方程
    for(int i=1; i<m; ++i){
        for(int j=1; j<=w; ++j){
            if(j >= weights[i])
                dp[i][j] = max(dp[i-1][j], dp[i-1][j - weights[i]] + values[i]);
            else
                dp[i][j] = dp[i-1][j];
        }
    }
    cout << dp[m-1][w] << endl;
    return 0;
}

上面的代码还可以进一步优化空间复杂度,dp数组使用的是二维数组,我们观察dp转移方程 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e s [ i ] ) dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+values[i]) dp[i][j]=max(dp[i1][j],dp[i1][jweight[i]]+values[i])发现 当前值 d p [ i ] [ j ] dp[i][j] dp[i][j]只与上一行的dp[i-1][j]和dp[i-1][j-weight[i]]有关,分别位于当前值的上方和左上方。因此我们可以只用一个一维数组表示一行的值,然后不断去更新每行的dp数组。
有一个易错点:遍历顺序。如果我们还用上面的遍历顺序,对于二维数组来说肯定是没什么问题,但是对于优化成一维数组来说就有问题了。问题在于上面的代码j是正序遍历,那么我们在当前行i更新dp数组值会先更新j值较小的dp[j],而dp[j]的更新又可能依赖于上一行的dp[j-weight[i]],但此时较小的dp[j-weight[i]]是在当前行已被更新,所以正序遍历不行。对于重量j,采用逆序遍历就可以解决这个问题,对于当前dp[j],dp[j-weight[i]]是未更新的。

# include<iostream>
# include<vector>

using namespace std;

int main(){
    int m, w; // m为物品数,w为背包容量
    cin >> m >> w;
    vector<int> weights(m); // 物品重量
    vector<int> values(m); // 物品价值
    for(int i=0; i<m; ++i)
        cin >> weights[i];
    for(int i=0; i<m; ++i)
        cin >> values[i];
    // 创建01背包的dp数组并初始化
    vector<int>dp(w+1);
    for(int i=weights[0]; i<=w; ++i)
        dp[i] = values[0];
    // dp方程
    for(int i=1; i<m; ++i)
        for(int j=w; j>=weights[i]; --j)
                dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);

    cout << dp[w] << endl;
    return 0;
}

2、分割等和子集 416

这题本质上就是将容量为数组和一半的背包填满。背包体积是 数组和的一半,物品、物品价值和物品重量都是数组元素,直接套用01背包代码就好。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int s = accumulate(nums.begin(), nums.end(), 0); // 数组和
        int n = nums.size();
        // 和为奇数 或 长度为1
        if(s % 2 || n < 2)
            return false;
        s /= 2; // 子集的和
        vector<int> dp(s+1);
        for(int num : nums)
            for(int i=s; i>=num; --i)
                dp[i] = max(dp[i], dp[i-num] + num); // 背包
        return dp[s]==s; 
    }
};

二、写在后面

01背包是个非常经典的动态规划问题,需要熟练掌握。01背包不仅可以求 背包能被的最大价值,还可以求这个背包是否可以装满。


http://www.kler.cn/a/534387.html

相关文章:

  • MongoDB学习笔记-解析jsonCommand内容
  • 【论文精读】Taming Transformers for High-Resolution Image Synthesis
  • 使用Pygame制作“贪吃蛇”游戏
  • JavaScript前后端交互-AJAX/fetch
  • 基于Springboot+vue的租车网站系统
  • 【LeetCode】5. 贪心算法:买卖股票时机
  • 苹果再度砍掉AR眼镜项目?AR真的是伪风口吗?
  • UE制作2d游戏
  • 《AI重塑网络开发:用户界面设计的革新之路》
  • 【分布式架构理论3】分布式调用(2):API 网关分析
  • ACK One 如何通过 GitOps DevOps 实现高效 CI/CD 流水线?
  • 深度学习-100-RAG技术之最简单的RAG系统概念和效果优化提升方向
  • (2025,LLM,下一 token 预测,扩散微调,L2D,推理增强,可扩展计算)从大语言模型到扩散微调
  • 旋钮屏设备物联网方案,ESP32-C3无线通信应用,助力设备智能化升级
  • 输入类控件和多元素控件【QT】
  • 使用Selenium和Jsoup框架进行Java爬虫
  • vue中嵌入iframe
  • 【BUUCTF杂项题】荷兰宽带数据泄露、九连环
  • Houdini subuv制作输出阵列图
  • 如何使用 Python 和 SQLAlchemy 结合外键映射来获取其他表中的数据
  • OpenCV:特征检测总结
  • k8s服务发现有哪些方式?
  • 【产品小白】什么是微服务
  • 基于微信小程序的课堂点名系统springboot+论文源码调试讲解
  • AURIX TC275学习笔记3 官方例程 (UART LED WDT)
  • mysql和oracle取Group By 第一条