[动态规划]---背包问题

news2024/12/26 3:56:04

前言

作者:小蜗牛向前冲

专栏:小蜗牛算法之路

 专栏介绍:"蜗牛之道,攀登大厂高峰,让我们携手学习算法。在这个专栏中,将涵盖动态规划、贪心算法、回溯等高阶技巧,不定期为你奉上基础数据结构的精彩算法之旅。一同努力,追逐技术的星辰大海。"

 

一、【模板】0/1背包

1、题目描述

描述
你有一个背包,最多能容纳的体积是V。
现在有n个物品,第i个物品的体积为v;,价值为wi。
(1)求这个背包至多能装多大价值的物品?
(2)若背包恰好装满,求至多能装多大价值的物品?
输入描述:
第一行两个整数n和V,表示物品个数和背包体积。
接下来n行,每行两个数u;和u;,表示第i个物品的体积和价值。
1≤n, V,vi, wi< 1000
输出描述:
第一行输出第一问的答案,
输出有两行,(
第二行输出第二问的答案,如果无
解请输出0。
示例1

输入:

3 5
2 10
4 5
1 4

复制输出:

14
9

复制说明:

装第一个和第三个物品时总价值最大,但是装第二个和第三个物品可以使得背包恰好装满且总价值最大。 

示例2

输入:

3 8
12 6
11 8
6 8

复制输出:

8
0

复制说明:

装第三个物品时总价值最大但是不满,装满背包无解。 

备注:

要求O(nV)的时间复杂度,O(V)空间复杂度

 

对于背包问题的解决方法 就是动态规划的思路:

2、状态表示

根据经验+题目要求:我们以前想的最多的是,dp[i]表示,从前i个物品中选,所有选法中,挑出礼物最大的价值。但是这里发现不行,因为这里还体积的限制。

对于0/1背包是有二种问法的,选出最大价值,不超过体积或者正好等于体积容量。

  • dp[i][j],表示从前i个物品中选择,总体积不超过j,所有选法中,能挑选出来的最大礼物的价值。
  • dp[i][j],表示从前i个物品中选择,总体积正好等于j,所有选法中,能挑选出来的最大礼物的价值。

3、状态转移方程 

 根据最后一步进行情况讨论:

  • 选第不i个物品:dp[i-1][j] 
  • 选择第i个物品:w[i]+dp[i-1][j-v[i]](这里的w表示价值,v表示体积)

从而推出状态方程:

dp[i][j]  = max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])

 4、初始化

这里我们选择的一个二维dp,为dp多增加一行,初始化为0即可。

5、填表顺序

 由于状态转移方程可以知道从上往下填表就可以了。

6、返回值

dp[n][v]

7、解题代码

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

int main() {
    int n, V;
    cin >> n >> V;
    vector<int> w(n + 1), v(n + 1);
    // 这里为了下面填表方便,从1位置开始填写
    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> w[i];
    }

    // 背包问题
    // 创建dp表
    vector<vector<int>> dp(n + 1, vector<int>(V + 1));

    // 第一问
    // 填表
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= V; j++) {
            // i位置不选择
            dp[i][j] = dp[i - 1][j];
            if (j - v[i] >= 0) {
                dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
            }
        }
    }
    cout << dp[n][V] << endl;

    // 第二问
    // 重新设置dp
    dp.clear();
    dp.resize(n + 1, vector<int>(V + 1, 0));

    // 初始化, dp[0][0] 应该是0,因为没有物品的时候重量为0
    dp[0][0] = 0;
    for (int j = 1; j <= V; j++) dp[0][j] = -1;

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= V; j++) {  // 注意这里从0开始
            // i位置不选择
            dp[i][j] = dp[i - 1][j];
            if (j - v[i] >= 0 && dp[i - 1][j - v[i]] != -1) {
                dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
            }
        }
    }

    cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;
}

这里我们还可以通过滚动数组的方式,将二维dp,优化为一维的dp 

其实非常简单,就将所有的横坐标删除,填表我们从右往左进行填表

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

int main() {
    int n, V;
    cin >> n >> V;
    vector<int> w(n + 1), v(n + 1);
    // 这里为了下面填表方便,从1位置开始填写
    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> w[i];
    }

    // 背包问题
    // 创建dp表
    vector<int> dp(V + 1);

    // 第一问
    // 填表
    for (int i = 1; i <= n; i++) {
        for (int j = V; j >= v[i]; j--) 
        {
            dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
        }
    }
    cout << dp[V] << endl;

    // 第二问
    // 重新设置dp
    dp.clear();
    dp.resize(V + 1, 0);

    // 初始化
    dp[0] = 0;
    for (int j = 1; j <= V; j++) dp[j] = -1;

    for (int i = 1; i <= n; i++) {
        for (int j = V; j >= v[i]; j--) 
        {
            if(dp[j - v[i]] != -1) {
                dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
            }
        }
    }

    cout << (dp[V] == -1 ? 0 : dp[V]) << endl;
}

这里温馨提醒一下:不要强行去解释 滚动后的数组的状态表示和状态方程,这里没有必要(何必涂增烦恼)。

二、0/1背包刷题 

话不多说,勤学多练

1、分割等和子集

这里的关键是我们要进行转换,这里我们要求分割后 等和子集,我们这里就可以转换为,数组和为sum,也就是说如果我们能够选择出一个sum/2来,就可以分割为一个等和子集

你们在这里就可以想到,就是从数组中挑选数组,凑成sum/2,数组中的每个数都是可以选择或者不选,这个不就0/1背包问题吗?我们就可以顺着这个思路去写动态规划。 

class Solution {
public:
    bool canPartition(vector<int>& nums)
    {

        //这里我们要转换为0/1背包问题
        //在数组中选择一些数和等于sum/2
        int n = nums.size();
        int sum = 0;
        for(int i = 0;i<n;i++)
        {
            sum +=nums[i];
        }
        if(sum%2)//不能均分为二部分,肯定不能分割
        {
            return false;
        }

        //状态表示:dp[i][j]:在前i位置挑选,在所有选发中能够凑成j这个数(true,false)
        vector<vector<bool>> dp(n+1,vector<bool>(sum/2+1));

        //初始化
        for(int i = 0;i<=n;i++)
        {
            dp[i][0] = true;
        }
        
        //填表
        for(int i = 1;i<=n;i++)
        {
            for(int j = 1;j<=sum/2;j++)
            {
                //i位置不选
                dp[i][j] = dp[i-1][j];
                if(j>=nums[i-1])//这里要注意下标的映射
                {
                    dp[i][j] = dp[i][j]||dp[i-1][j-nums[i-1]];
                }
            }
        }

        //返回
        return dp[n][sum/2];
    }
};

空间优化:

class Solution {
public:
    bool canPartition(vector<int>& nums)
    {

        //这里我们要转换为0/1背包问题
        //在数组中选择一些数和等于sum/2
        int n = nums.size();
        int sum = 0;
        for(int i = 0;i<n;i++)
        {
            sum +=nums[i];
        }
        if(sum%2)//不能均分为二部分,肯定不能分割
        {
            return false;
        }

        //状态表示:dp[i][j]:在前i位置挑选,在所有选发中能够凑成j这个数(true,false)
        vector<bool> dp(sum/2+1);

        //初始化
        dp[0] = true;

        //填表
        for(int i = 1;i<=n;i++)
        {
            for(int j = sum/2;j>=nums[i-1];j--)
            {

                dp[j] = dp[j]||dp[j-nums[i-1]];
            }
        }

        //返回
        return dp[sum/2];
    }
};

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2086476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何让ChatGPT说话更像人类

ChatGPT在多个领域展现了卓越的能力&#xff0c;但对话中仍不可避免地带有一定的机械感。幸运的是&#xff0c;OpenAI推出的GPTs功能可以让用户自定义prompt。最近&#xff0c;我发现了其中一个GPTs&#xff0c;它能让ChatGPT的对话更加贴近真人&#xff0c;现在就来与大家分享…

【生日视频制作】农村文化墙广告标语AE模板修改文字软件生成器教程特效素材【AE模板】

生日视频制作教程农村文化墙广告标语AE模板修改文字软件生成器 怎么如何做的【生日视频制作】农村文化墙广告标语AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤&#xff1a; 安装AE软件 下载AE模板 把AE模板导入AE软件 修改图片或文字 渲染出视频

复杂网络|节点重要性评价指标

author: xiao黄 time: 2024-08-28 公众号: 复杂网络与网络科学 CSDN: https://blog.csdn.net/Python_Matlab评价节点重要性算法的指标有多种&#xff0c;如基于信息传播方面的动力学模型、单调性、Distinct Metric以及基于网络脆弱性和鲁棒性的方法等。不同的评价指标所考虑的角…

Java Web_00001

目录 Web项目介绍网页的组成部分 HTMLHTML简介HTML示例HTML文件的书写规范HTML标签标签介绍标签的语法&#xff1a;常用标签font特殊字符标题标签超链接列表标签img标签表格标签跨行跨列表格iframe框架标签(内嵌窗口)表单标签表单的显示表单格式化表单提交细节 其他标签 CSSCSS…

羟基“消失术”,化学合成中的巧妙方法

羟基(-OH)是一个很常见的官能团&#xff0c;在有机合成的转化过程中往往起到桥梁作用。在化合物合成过程中由于合成选择的原因通常会先引入一些基团&#xff0c;以降低合成化合物的难度以及提高其产率。而羟基的引入或者生成是比较常见的。羟基化方法有很多&#xff0c;其中包括…

JDBC和Mybatis中的批处理

src目录下创建jdbc.properties mysql驱动5.1.6之后,只需要配置url,username,password mysql 5.1.6之后可以无需Class.forName("com.mysql.jdbc.Driver") * 从jdk1.5之后可以通过配置文件来配置 * 会自动加载mysql驱动jar包下META-INF/services/java.sql.Driver文本中…

Python深度学习股价预测、量化交易策略:LSTM、GRU深度门控循环神经网络|附代码数据

全文链接&#xff1a;https://tecdat.cn/?p37539 原文出处&#xff1a;拓端数据部落公众号 分析师&#xff1a;Shuo Zhang 本文以上证综指近 22 年的日交易数据为样本&#xff0c;构建深度门控循环神经网络模型&#xff0c;从股价预测和制定交易策略两方面入手&#xff0c…

8月29日

思维导图 作业&#xff1a; 仿照string类&#xff0c;实现myString 代码 mystring.h #ifndef MYSTRING_H #define MYSTRING_H#include <iostream> #include<string.h>using namespace std;class myString { private:char *str;int size;public://无参构造myStr…

在自己的数据集上测试coco评价指标——以Mar20为例

参考&#xff1a; 1.在自己的数据集上调用cocoapi计算map 2. COCO Result Format 3.COCO result json 之前的模型都是在COCO数据集上训练&#xff0c;数据集的标注以及结果的生成格式都是按照官方的格式组织的&#xff0c;调用cocoapi和官方下载的instance_val2017.json计算就可…

Win11 操作(七)声音降噪

前言 为了听脚步和不外放声音影响到女朋友休息&#xff0c;于是买了S21头戴式耳机&#xff0c;虽然目的都达到了&#xff0c;但是又有新问题出现 损害队友听力 由于天气炎热&#xff0c;家里都开着风扇&#xff0c;但是耳机没有降噪功能所以我的麦噪音极大&#xff0c;这就导…

OpenLayers3, 设置地图背景

文章目录 一、前言二、代码实现三、总结 一、前言 本文基于OpenLayers3&#xff0c;实现地图加入背景图的功能。 二、代码实现 <!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml"> <head><meta http-equiv"Content-Type"…

封装string

仿照string类&#xff0c;实现mystring #include <iostream> #include<string.h> using namespace std;class MyString {private:char *str; //记录c风格的字符串int size0; //记录字符串实际长度public://无参构造&#xff1a;定义了一个字符串MyStri…

Noise, Dynamic Range and Bit Depth in Digital SLRs --- 数字单反相机中的噪点、动态范围和位深

系列文章目录 文章目录 系列文章目录前言数字单反相机中的噪点、动态范围和位深二、噪声的来源2.1 光子散粒噪声2.2 读出噪声2.3 模式噪声2.4 热噪声2.5 像素响应不均匀性&#xff08;PRNU&#xff09;2.5 量化误差 前言 Noise, Dynamic Range and Bit Depth in Digital SLRs …

golang私有仓库遇到的问题记录

问题1: is this a git repository? 原因&#xff1a;git保存了错误的用户名密码 我是mac系统&#xff0c;在启动台-》其他-》钥匙串&#xff0c;找到git的登录信息&#xff0c;将错误的钥匙串删除即可。 问题2: remote: The project you were looking for could not be found…

HarmonyOS鸿蒙开发( Beta5版)Navigation组件常规加载与动态加载

简介 应用在加载页面时&#xff0c;如果引入暂时不需要加载的模块&#xff0c;会导致页面加载缓慢和不必要的内存占用。例如当页面使用Navigation组件时&#xff0c;主页默认加载子页面&#xff0c;此时若子页面使用了Web组件&#xff0c;则会提前加载Web相关的so库&#xff0…

Mybatis:基础巩固-DQL

目录 一、概述二、数据准备三、基础查询四、条件查询五、聚合函数六、分组查询七、排序查询八、分页查询九、DQL执行顺序 一、概述 主要用于对数据的查询操作&#xff0c;使用的关键字SELECT SELECT 字段列表 FROM 表名 WHERE 条件列表 GROUP BY 分组字段 HAVING 分组后条件列表…

Renesa Version Board和微信小程序通信

目录 概述 1. 系统框架结构 1.1 功能介绍 1.2 系统框图 2 微信小程序开发 2.1 UI介绍 2.2 代码实现 3 功能实现 3.1 通信协议 3.2 系统测试 概述 本文主要介绍基于Renesa Version Board&#xff0c;采集多个传感器数据&#xff0c;并将这些数据通过蓝牙模块发送微信…

《黑神话悟空》:国产3A游戏的崛起与AI绘画技术的融合

一、游戏简介 近年来&#xff0c;国产3A游戏《黑神话悟空》以其精美的画面、丰富的剧情和独特的文化底蕴吸引了众多玩家的关注。这款游戏以中国古典名著《西游记》为背景&#xff0c;讲述了孙悟空历经磨难&#xff0c;最终成长为斗战胜佛的故事。在游戏制作过程中&#xff0c;开…

办公必备,免费的在线万能格式转换工具

在当今数字化时代&#xff0c;文件格式转换已成为办公和日常生活中不可或缺的一部分。随着各种文件格式的不断涌现&#xff0c;人们对于高效、便捷的文件转换工具的需求日益增长。小编将为大家介绍几款免费的在线万能格式转换工具&#xff0c;帮助大家轻松应对各种文件转换需求…

golang uint8 转int出现ascll码值

在Golang中&#xff0c;uint8类型被用来表示ASCII码值。 结果是51 如果是uint8(3)的话结果还是3 所以在我们想把一个uint8类型的字符数字转换为int类型时需要特殊处理 减去对应ASCII码’0’的值 结果就是3了