01背包问题再探

news2024/11/18 6:40:54

原题:

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

题解:

状态表示:

f[i][j]表示选择第i件物品,容量不超过j的最大价值。

状态计算:

将f[i]表示的所有选法分成两大类

1. 选法中不含 i , 即从 1 ~ i-1中选,且总体积不超过j,即 f[i-1]

2. 选法中包含 i ,即从 1 ~ i 中选,包含 i,且总体积不超过 j

可以先把第 i 个物品拿出来,即从第 1 ~ i-1中选,且总体积不超过 j-v[i],即f[i-1][j-v[i]]+w[i]

得到状态转移方程:

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

完整代码: 

#include<iostream>
#include<cstdio>

using namespace std;

const int N = 1e3+10;

int f[N][N];
int v[N],w[N];
int n,m;

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            f[i][j] = f[i-1][j];
            if(j>=v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
        }
    
    cout<<f[n][m]<<endl;
    return 0;
}

这里借用一下博主滚动数组(简单说明)_儒rs的博客-CSDN博客_滚动数组的图片

在这里插入图片描述

回顾背包问题,或许还会有以下疑惑:

1.如何判断当前选了第i件物品,总体积不会超过背包容量?

答:状态表示:从第1到i件物品中选,且总体积不超过j的所有选法的集合属性是最大值,状态转移方程的判断j>=v[i],当选第i件物品带来的价值更大时,此时的状态表示为f[i-1][j-v[i]]+w[i],所选物品的总体积已经是给第i件物品留出空间来之后的体积j-v[i]。后续方案有更大值并不一定会继承上一个状态的方案(这里的方案代表每件物品的取舍),因为在dp中最优方案是不断变化的,

f[i][j]=f[i+1][j];
if(j>=v[i])f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);

可能在这一一步中 f[i][j]的最优选择是选择了第i件物品的
但是在后续递推的过程中,这个方案可能就被淘汰掉

2.如何得知取得最大价值的方案(字典序最小为例)

答:i是所有选择第i件物品的方案,i-1是所有不选择i的方案。

同第一问,因为dp过程中最优方案不断变化,实际上我们是从后往前推,第n个物品是否选,n-1个物品是否选...而要求字典序最小的最优方案是第1个物品能选则选,第2个物品能选则选...所以需要先从后往前求出最大价值,然后才可以从前往后推出方案

#include<iostream>
#include<cstdio>

using namespace std;

const int N = 1010;

int f[N][N];
int w[N], v[N];
int n, m;

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d%d", &v[i], &w[i]);
    
    for(int i = n; i; i --){
        for(int j = 0;j <= m; j++){
            f[i][j] = f[i+1][j];
            if(j >= v[i]) f[i][j] = max(f[i][j], f[i+1][j-v[i]]+w[i]);
        }
    }
    
    for(int i=1, j=m; i<=n; i++){
        if(f[i][j] == f[i+1][j-v[i]]+w[i] && j >= v[i]){
            cout<<i<<" ";
            j -= v[i];
        }
    }
    
    
    return 0;
}

3.既然最大值一定在f [ i ] [ m ]中取,为什么还要枚举 j 从1到m

答:dp是由小状态最优推出总体状态最优的(局部最优推整体最优),第二维状态涉及背包体积问题,受限(具体看第一问)。且dp中最优方案不是前一个状态决定后就一成不变的,可能前一个状态选择了第i件物品,后续递推过程中这个方案就被淘汰掉。

4.使用滚动数组将二维转化为一维问题,为什么可以省略,为什么要逆序枚举容量

状态转移方程第一维只用到了前一个状态,所以直接覆盖就可以。第二维用到的状态必定小于当前的j(j>=j-v[i])。

#include<iostream>
#include<cstdio>

using namespace std;

const int N = 1e3+10;

int f[N][N];
int v[N],w[N];
int n,m;

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
    
    for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--){
            f[j] = max(f[j],f[j-v[i]]+w[i]);
        }
    
    cout<<f[n][m]<<endl;
    return 0;
}

为什么一维情况下枚举背包容量需要逆序?在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。

例如,一维状态第i轮对体积为 33 的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该是f[i - 1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。

简单来说,一维情况正序更新状态f[j]需要用到前面计算的状态已经被污染,逆序则不会有这样的问题。

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

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

相关文章

盖子的c++小课堂——第五讲:for 循环

前言 hi&#xff0c;大家好&#xff0c;我是盖子的盖&#xff0c;最近大家都放假了吗&#xff0c;反正我还没有&#xff0c;我们期末考才刚开始考呜呜呜&#xff0c;真羡慕那些放假了的童鞋们~~(╥╯^╰╥)~~ 好啦&#xff0c;废话不多说&#xff0c;开始今天的小课堂吧~~ 上…

厚积薄发打卡Day113:Debug设计模式:设计原则(一)<开闭原则、依赖倒置、单一职责>

厚积薄发打卡Day113&#xff1a;Debug设计模式&#xff1a;设计原则&#xff08;一&#xff09;<开闭原则、依赖倒置、单一职责> 开闭原则 定义 一个软件实体如类、模块和函数应该对扩展开放&#xff0c;对修改关闭。其优点&#xff1a;提高软件系统的可复用性及可维护…

JavaEE多线程-创建线程(Thread)

目录一、线程(Thread)1.1 Thread类中的构造方法1.2 启用线程的方法二、创建第一个多线程三、多线程并发执行简单演示四、多线程并发执行的优势五、Thread的常见构造方法和属性5.1 属性5.2 方法六、中断线程七、线程等待一、线程(Thread) 线程是操作系统中的概念. 操作系统内核…

LeetCode二叉树经典题目(六):特殊位置构造二叉树

目录 21. LeetCode404. 左叶子之和 22.LeetCode513. 找树左下角的值 23. LeetCode112. 路径总和 24. LeetCode113. 路径总和 II 25. LeetCode106. 从中序与后序遍历序列构造二叉树 26. LeetCode105. 从前序与中序遍历序列构造二叉树​编辑 27. LeetCode654. 最大二叉树 …

LED、Mini LED、Micro LED、LCD、OLED技术

1、传统led、miniled、microled的异同 2、OLED OLED&#xff08;Organic Light-Emitting Diode&#xff09;&#xff0c;又称为有机电激光显示、有机发光半导体&#xff08;Organic Electroluminescence Display&#xff0c;OLED&#xff09;。OLED属于一种电流型的有机发光器…

S32K144-hello_word点灯

官方提供了很多的参考例程&#xff0c;每个历程分别配置了不同的外设&#xff0c;这里挨个尝试解读一下。 示例效果 RGB红灯绿灯交替闪烁。 导入示例 示例文件所在目录&#xff1a; 该示例使用PCC和PORT模块交替切换两个LED。 硬件连接 配置引脚功能 生成代码 S32DS自带引…

C#上位机基础学习_登录窗体的创建方法和步骤

C#上位机基础学习_登录窗体的创建方法和步骤 本次和大家分享如何制作一个简单的登录窗体。具体的方法和步骤可以参考以下内容: 如下图所示,打开Visual Studio 2019,新建一个Windows 窗体应用(.NET Framework), 如下图所示,在窗体中添加Label标签、Text文本框、Button按…

Java中常用API总结(5)—— Object类中的深克隆和浅克隆

对象克隆一、前言二、浅克隆1.概述2.实例1️⃣思路2️⃣继承cloneable接口底层原理3️⃣重写clone方法底层原理3.代码实现三、深克隆1.概述2.实例3.代码实现四、结语一、前言 本文将详细讲述Object类中的对象克隆方法&#xff0c;其中包含深克隆和浅克隆&#xff0c;两者有一定…

算法竞赛100天第2天——STL IN C++(算法竞赛必备知识总结汇总)

本文已收录于专栏 &#x1f332;《百日算法竞赛》&#x1f332; 目录 前言&#xff1a; 序列容器 序列的要求&#xff1a; 1.vector vector常用方法 vector遍历 2、deque 头文件 方法 3、list 头文件 方法 4、queue 方法&#xff1a; 关联容器 1、map 2、se…

SWPUCTF 2022新生赛部分wp

&#x1f60b;大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白。 本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;一起学习&#x1f497;&#xff0c;一起进步⭐️。⭐️此后如竟没有炬火&#xff0c;我便是唯一的光。⭐️[SWPUCTF 2022 新生赛]ez_ez_ph…

【算法】链表

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录链表数组转链表链表转数组往链表的前面增加一个节点获得指定位置的值在链表的末尾增加一个节点在…

我最近发现的一些问题。

‍‍ 大家好&#xff0c;我是小z&#xff0c;也可以叫我阿粥~这段时间看了不少分析报告&#xff0c;启发颇多。也发现有一些普遍存在且很容易误导分析师的问题。在聊问题之前&#xff0c;先给大家分享一个&#xff08;我刚写的&#xff09;小故事&#xff1a;有一个学生…

Pytorch 多项式拟合

目录 1、训练误差和泛化误差 2、独立同分布假设 3、欠拟合和过拟合 4、多项式回归 1、训练误差和泛化误差 训练误差&#xff08;training error&#xff09;是指&#xff0c; 模型在训练数据集上计算得到的误差。 泛化误差&#xff08;generalization error&#xff09;是指…

【OpenCV 例程 300篇】255.OpenCV 实现图像拼接

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】255.OpenCV 实现图像拼接 6.2 OpenCV 实现图像拼接 OpenCV中图像的数据结构是Numpy数组&#xff0c;使用切片方法可以实现图像的裁剪&#xff0c;使用数组堆叠方法可以实现图像的拼接。 Numpy 函数…

K8S Replication Controller 示例

K8S Replication Controller Replication Controller可确保在任何时间运行指定数量的pod副本。换句话说&#xff0c;ReplicationController确保一个pod或一组同类pod始终处于可用状态。 可以理解为Replication Controller &#xff08;RC&#xff09;是基于 K8S Pod对象之上的…

PR采购申请启用灵活工作流配置

目录 1. 检查系统基础工作流配置 2. 激活标准场景模版 WS02000458 3. 维护收件箱操作按钮文本 4. 维护任务代理及激活事件 5. 配置Inbox审批页面 6. PR灵活工作流启动配置 7. 为流程设置代理人 8. 配置工作流场景 9. 可扩展部分 官方Help文档路径&#xff1a; SAP He…

算法_杨氏矩阵_杨氏矩阵算法_剑指offer

目录 一、问题描述 二、问题分析 三、算法设计 ​四、代码实现 一、问题描述 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N);…

【目标检测】Cascade RCNN中的两个常见问题

一、关于mismatch问题 在training阶段和inference阶段使用不同的阈值很容易导致mismatch&#xff0c;什么意思呢&#xff1f; 在training阶段&#xff0c;由于给定了GT&#xff0c;所以可以把与GT的IoU大于阈值的proposals作为正样本&#xff0c;这些正样本参与之后的bbox回归…

【C语言】深入浅出讲解函数栈帧(超详解 | 建议收藏)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;凡人修C传 &#x1f4ac;总结&#xff1a;希望你看完之后&…

基于机器学习与协同过滤的图书管理推荐系统

基于机器学习与协同过滤的图书推荐系统 一、系统结构图 二、Demo示例 完整源码可联系博主微信【1257309054】 点我跳转 三、K-means聚类机器学习推荐算法 1、原理 从数据库中 1、首先获取书籍类别 2、获取用户注册时勾选喜欢的类别&#xff0c;勾选的为1&#xff0c;否则…