【AcWing】AcWing 2. 01背包问题

简介: 目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解

一、题目

1、原题链接

2. 01背包问题 - AcWing题库

2、题目描述

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

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

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

输出最大价值。


输入格式

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

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


输出格式

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


数据范围

0<N,V≤1000

0<vi,wi≤1000


输入样例

4 5

1 2

2 4

3 4

4 5


输出样例:

8


二、解题报告

思路来源:AcWing 2. 01背包问题(闫氏DP分析法) - AcWing


y总yyds


1、思路分析

二维dp


1)创建dp数组,dp[i][j]代表容量为j的背包可以放物品1~i中的任意物品背包所能达到的最大价值。

2)dp[i][j]的求取是两种情况中的最大值,第一种情况是dp[i-1][j],代表不放第i件物品,下一次只从前i-1件物品中来选择物品放入;第二种情况是dp[i-1][j-v[i]]+w[i],代表放第i件物品,所以背包中就必须得有i物品的位置,所以背包容量得减去i物品的体积,同时背包的价值也要加上i物品的价值,因为本次已经选了i物品,所以下一次就不能再选i物品,所以i-1。我们选择这两种情况中的最大值,作为dp[i][j]的最大值。

所以,递推方程为:dp[i][j]=max(dp[i-1][j],dp[i-1] [j-v[i]]+w[i])

3)针对背包容量为0或物品为0时(也就是数组第一列和第一行),dp的数组的值初始化均为0,其他值都可通过递推式求得(通过它左上方某个元素和它正上方的元素通过递推式求得)。

4)两层for循环遍历物品和背包容量,遍历顺序怎样都可以,因为我们无论怎样遍历都可以通过递推方程算出相应的值。对dp数组赋值,最后输出dp[N][V],即为所求。


一维dp

1)在二维dp的情况下,我们可以优化一下空间,将二维数组压缩为一维数组,这个一维数组始终存储的是上一行的dp值,需要算本次的dp值,只要通过上一层的dp值也就是原数组值进行逐个更新即可。

2)思路与二维dp相同,所以递推方程也很类似:dp[j]=max(dp[j],dp[j-v[i]]+w[i]),仅仅是去掉了第一维的下标,将多个物品压缩在一行,每次更新某个元素,我们更新成了下一行该位置元素的值。

3)两层for循环分别遍历物品和背包,不可颠倒,而且背包容量需倒序遍历。因为我们将物品这一维给去掉了,所以第一次遍历数组中存放的是物品1在不同容量背包下所能达到的最大价值,我们需要遍历所有的物品在不同容量背包中所能带来的最大价值,而每次遍历物品都会依托于它的上一行,也就是它的上一个物品的所有dp值(对于二维dp来说就是上一行的值),如果先遍历背包的话,我们当前dp数组存储的不是上一行的dp值,而是一列值(就是在一定背包容量下,不同物品所带来的价值),这显然与我们预期不符。而遍历顺序由于在二维递推中,当前值取决于它左上方某个元素和它正上方的元素,而一维递推直接把它所在这一行和它上一行压缩在了同一行中,所以递推只取决于了它左边的元素,针对某一次更新dp数组,首先,我们可以知道现在未更新的dp数组存储的是上一个物品的所有情况的最大价值,我们需要在此基础上更新出我们该行的dp值,而且是根据左边元素进行更新,如果我们也从左边开始更新,先更新数组第一个元素,然后数组第一个元素的值已经更新成了新的值,接下来,第二个元素更新,这时候发现它的左边元素(也就是第一个元素)已经更新了,不是上一行的值了,而我们需要上一行的值来进行更新,所以就会发生错误,我们从右往左开始倒序遍历就不会出现这种情况(这就很类似数组的插入操作,我们插入了一个元素到数组中,我们需要从插入位置开始逐个更新数组值,我们也是从数组最后一个元素来操作,依次向后挪,如果从插入位置开始挪的话,后面的值就都被前面的元素值给覆盖掉了)。


4)对dp数组赋值,最后输出dp[V],即为所求。

2、时间复杂度

时间复杂度均为O(n^2)


3、代码详解

二维dp


#include <iostream>

using namespace std;

int dp[1010][1010];

int v[1010];

int w[1010];

int main()

{   int N,V;

   cin>>N>>V;

   //注意下标,物品编号从1开始,下标为0的物品代表什么也不放

for(int i=1;i<=N;i++){

 cin>>v[i]>>w[i];

}

//i从1开始,i从0开始会导致数字下标越界

for(int i=1;i<=N;i++){

 for(int j=0;j<=V;j++){

  //如果当前背包还能放下第i件物品才与不放i物品时背包的最大价值比较

    if(j-v[i]>=0)

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

  //如果放不下物品i,就不放物品i

    else

      dp[i][j]=dp[i-1][j];

 }

}

   cout<<dp[N][V];

return 0;

}


一维dp


#include <iostream>

using namespace std;

int dp[1010];

int v[1010];

int w[1010];

int main()

{   int N,V;

   cin>>N>>V;

   //注意下标,物品编号从1开始,下标为0的物品代表什么也不放

for(int i=1;i<=N;i++){

 cin>>v[i]>>w[i];

}

//i从1开始,i从0开始会导致数字下标越界

for(int i=1;i<=N;i++){

 for(int j=V;j>=0;j--){

  //如果当前背包还能放下第i件物品才与不放i物品时背包的最大价值比较

    if(j-v[i]>=0)

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

  //如果放不下物品i,就不放物品i

    else

      dp[j]=dp[j];

 }

}

   cout<<dp[V];

return 0;

}


在上面代码的基础上可以再简化一下代码,得到最终优化版代码如下:


#include <iostream>

using namespace std;

int dp[1010];

int v[1010];

int w[1010];

int main()

{   int N,V;

   cin>>N>>V;

for(int i=1;i<=N;i++){

 cin>>v[i]>>w[i];

}

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];

return 0;

}

目录
相关文章
|
Web App开发 缓存 JavaScript
【安装指南】nodejs下载、安装与配置详细教程
这篇博文详细介绍了 Node.js 的下载、安装与配置过程,为初学者提供了清晰的指南。读者通过该教程可以轻松完成 Node.js 的安装,了解相关配置和基本操作。文章首先介绍了 Node.js 的背景和应用场景,随后详细说明了下载安装包、安装步骤以及配置环境变量的方法。作者用简洁明了的语言,配以步骤图示,使得读者能够轻松跟随教程完成操作。总的来说,这篇文章为初学者提供了一个友好的入门指南,使他们能够顺利开始使用 Node.js 进行开发。
4616 2
【安装指南】nodejs下载、安装与配置详细教程
|
域名解析 缓存 网络协议
提升你的外国服务器网站国内访问速度~
由于众所周知的原因,国内访问国外的服务器速度较慢。在没有特殊线路(直连、CN2GIA等)的加持下,路由线路左绕右绕,严重影响国内访问速度。 能使用国内服务器当然是最好的,但是高昂的流量&带宽价格以及域名备案门槛让人劝退。所以,本文章提供的加速方案是针对线路一般的海外服务器网站访问速度慢的问题。
7369 0
提升你的外国服务器网站国内访问速度~
|
存储 测试技术 C语言
C语言实现链表的各种功能
本文详细介绍了如何使用C语言实现链表的各种功能,包括链表节点结构的定义与操作函数的实现。链表作为一种常用的数据结构,具有节点自由插入删除、动态变化等特点。文中通过`link_list.h`和`link_list.c`两个文件,实现了链表的初始化、插入、删除、查找、修改等核心功能,并在`main.c`中进行了功能测试。这些代码不仅展示了链表的基本操作,还提供了丰富的注释帮助理解,适合作为学习链表的入门资料。
|
机器学习/深度学习 资源调度 自然语言处理
Transformer中高级位置编码的介绍和比较:Linear Rope、NTK、YaRN、CoPE
在NLP中,位置编码如RoPE、CoPE等增强模型对序列顺序的理解。RoPE通过旋转矩阵编码位置,适应不同距离的相对位置。线性旋转、NTK和YaRN是RoPE的变体,优化长序列处理。CoPE是动态的,根据序列内容调整位置编码,改善长距离依赖的捕捉。这些技术提升了模型在处理复杂语言任务时的性能。
422 5
|
11月前
|
前端开发
使用LangGraph构建多Agent系统架构!
【10月更文挑战第7天】
1521 0
|
监控 安全 数据安全/隐私保护
Rootkit工作原理及其检测方法
【8月更文挑战第31天】
686 0
|
存储 弹性计算 运维
深度解读:阿里云服务器ECS经济型e实例配置整理和性能参数表
阿里云推出经济型ECS e系列服务器,适用于个人开发者、学生和小微企业。该系列采用Intel Xeon Platinum处理器,支持多种CPU内存配比,性价比高,2核2G3M配置只需99元/年,新老用户不限量购买且续费不涨价。提供相同可用性SLA和安全标准,具备ESSD Entry云盘等企业级特性。适合中小型网站、开发测试和轻量级应用
|
机器学习/深度学习 算法 计算机视觉
YOLOv8改进 | 融合模块 | 用Resblock+CBAM卷积替换Conv【轻量化网络】
在这个教程中,介绍了如何将YOLOv8的目标检测模型改进,用Resblock+CBAM替换原有的卷积层。Resblock基于ResNet的残差学习思想,减少信息丢失,而CBAM是通道和空间注意力模块,增强网络对特征的感知。教程详细解释了ResNet和CBAM的原理,并提供了代码示例展示如何在YOLOv8中实现这一改进。此外,还给出了新增的yaml配置文件示例以及如何注册模块和执行程序。作者分享了完整的代码,并对比了改进前后的GFLOPs计算量,强调了这种改进在提升性能的同时可能增加计算需求。教程适合深度学习初学者实践和提升YOLO系列模型的性能。
|
SpringCloudAlibaba 监控 Dubbo
微服务结构及微服务远程调用
微服务结构及微服务远程调用
296 0
|
机器学习/深度学习 人工智能 自然语言处理
如何学习使用AIGC
如何学习使用AIGC