5. 多重背包问题 II(acwing)

news/2024/12/2 16:01:27/

文章目录

  • 5. 多重背包问题 II
    • 题目描述
    • 动态规划
      • 一维数组
        • 三重循环(超时)
        • 二进制优化(正确代码)
      • 二维数组
        • 三重循环(超时)
        • 二进制优化(超出内存限制)

5. 多重背包问题 II

题目描述

有 N种物品和一个容量是 V的背包。

第 i种物品最多有 si 件,每件体积是 vi,价值是 wi

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

输出最大价值。

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

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

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

数据范围
0<N≤1000

0<V≤2000

0<vi,wi,si≤2000

提示:
本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

动态规划

一维数组

三重循环(超时)

这段代码是用于解决多重背包问题的,它使用了动态规划(DP)的方法,并且通过二进制的思想对多重背包问题进行了优化。不过,请注意,这个代码并没有实际使用二进制优化,而是使用了一个普通的三重循环方法。下面我会注释这段代码,但也会提到二进制优化原理。

#include<bits/stdc++.h>  // 包含了几乎所有常用的库
using namespace std;// 定义三个数组,分别存储物品的体积、价值和数量
int v[1010], w[1010], s[1010];
// 定义一个一维数组dp,用于存储背包的容量与可达到的最大价值
int dp[2020];int main()
{int n, m;  // n为物品种类,m为背包容量cin >> n >> m;  // 输入物品数量和背包容量for(int i=1; i<=n; i++)cin >> w[i] >> v[i] >> s[i];  // 输入每个物品的体积、价值和数量// 遍历每种物品for(int i=1; i<=n; i++)// 从背包容量开始向下遍历,这样可以确保每个物品只选择一次for(int j=m; j>=w[i]; j--)// 遍历可以选择的物品数量,注意不超过背包容量和物品数量限制for(int k=0; k<=s[i] && k*w[i]<=j; k++)// 更新dp[j]为不选择或选择k个第i种物品的最大值// dp[j-k*w[i]]+k*v[i]表示选择k个第i种物品的价值dp[j] = max(dp[j], dp[j-k*w[i]]+k*v[i]);// 输出背包容量为m时的最大价值cout << dp[m];return 0;
}

上面的代码是对多重背包问题的直观解决方法,在这种方法中,我们对于每种物品,都尝试从0件到s[i]件之间的所有可能性,并更新状态dp[j]。这是一种朴素的方法,动态规划状态转移方程如下:

dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i]),其中k是选择的第i种物品的数量,这个方程表示背包容量为j时最大价值可以是不选当前物品的价值dp[j],或者选了k个当前物品的价值dp[j - k * w[i]] + k * v[i]

尽管这个代码段没有使用二进制优化,但是二进制优化的思想是:将每种类型的物品数量按照二进制拆分,即将每种物品分解为1件、2件、4件…,2k件(其中2k不超过s[i]),通过这种方式可以将多重背包问题转换为0/1背包问题来解决,从而优化时间复杂度。

这段代码是完整的,并且可以正确地解决多重背包问题,但在面对大数据集时可能不够高效,那时候二进制优化就显得非常必要了。在实际应用中,你可以根据数据的大小和时间复杂度要求,来决定是否使用二进制优化。

二进制优化(正确代码)

这段代码是多重背包问题的解决方案,使用了二进制优化方法。详细注释如下:

#include<bits/stdc++.h>
using namespace std;// 设置最大数组范围
const int z=25000;
// v数组保存每个拆分后的物品的价值,w数组保存每个拆分后的物品的体积
int v[z],w[z]; 
// dp数组用于动态规划,记录背包容量为j时可达到的最大价值
int dp[z]; int main()
{int n,m; // n为物品种数,m为背包容积cin>>n>>m; // 读入物品种数和背包容积int pos=0; // pos用于记录拆分物品的个数// 循环处理每种物品for(int i=1;i<=n;i++){int a,b,s; // a为体积,b为价值,s为数量cin>>a>>b>>s; // 读入体积、价值、数量int k=1; // 用于二进制拆分,初始为2的0次方,即1// 当物品数量大于等于k时进行拆分while(k<=s){// 按照二进制拆分原则,存储拆分后物品的体积和价值w[++pos]=a*k; // 拆分的体积是原体积的k倍v[pos]=b*k; // 拆分的价值是原价值的k倍// 减去已经拆分的数量s-=k;// k翻倍进行下一次二进制拆分k*=2;}// 如果还有剩余物品,则继续存储剩余的体积和价值if(s>0){w[++pos]=a*s; // 剩余的体积v[pos]=b*s; // 剩余的价值}}// 使用一维数组的动态规划求解,类似于01背包问题for(int i=1;i<=pos;i++) // 遍历所有拆分后的物品{// 从大到小遍历背包容量,确保每个物品只被计算一次for(int j=m;j>=w[i];j--){// dp[j]是不放这件物品,dp[j-w[i]] + v[i]是放这件物品// 更新dp[j]为这两者的较大值dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}}// 输出背包容量为m时,能装入物品的最大价值cout<<dp[m];return 0;
}

在这个多重背包问题中,每种物品可以有多件,但是数量有限。二进制优化的思路是将每种物品拆分成若干个组合,使得每个组合内物品的数量是2的幂次,这样可以通过少量的组合来组成任意数量的物品,减少了状态的数量。优化后使用动态规划求解时,就如同解决01背包问题一样,每个物品只会被选取一次。最后输出的 dp[m] 就是所求的最大价值。

二维数组

三重循环(超时)

这段代码是一个解决多重背包问题的例子,它使用了动态规划的方法。具体来说,它利用二维动态规划数组dp[i][j]来存储考虑前i种物品,当背包容量为j时所能达到的最大价值。下面是对这段代码的详细注释:

#include<bits/stdc++.h> // 包括STL库
using namespace std;// 定义数组,v[i]、w[i]和s[i]分别存储第i种物品的体积、价值和数量。
int v[1010], w[1010], s[1010];
// dp[i][j]表示考虑前i种物品,当背包容量为j时的最大价值。
int dp[2020][2020];int main()
{int n, m; // n是物品种数,m是背包容量cin >> n >> m; // 输入物品种数和背包容量// 读入每种物品的体积v[i]、价值w[i]和数量s[i]for(int i = 1; i <= n; i++)cin >> w[i] >> v[i] >> s[i];// 动态规划求解for(int i = 1; i <= n; i++) // 遍历所有物品{for(int j = 0; j <= m; j++) // 遍历所有可能的背包容量{for(int k = 0; k <= s[i] && k * w[i] <= j; k++) // 遍历当前物品可以选择的数量,确保不超过该物品的数量限制且背包容量不会超标{// dp[i][j]更新为考虑当前i种物品、容量为j时的最大价值// max函数比较不取当前物品的情况和取k个当前物品情况下的价值,选择较大者dp[i][j] = max(dp[i][j], dp[i-1][j-k*w[i]] + k*v[i]);}}}// 输出考虑n种物品,背包容量为m时的最大价值cout << dp[n][m];return 0;
}

动态规划原理解释

这个动态规划的过程是按照物品的种类进行的,对于每一种物品i,它都会尝试从0件到s[i]件进行选择,并更新状态dp[i][j]。这里的dp[i][j]代表考虑到第i种物品时,背包容量为j能够达到的最大价值。

  • dp[i-1][j-k*w[i]] + k*v[i]的意思是,如果当前考虑选择k个第i种物品,那么背包里剩余的容量为j-k*w[i],对应的最大价值是dp[i-1][j-k*w[i]](即在没有考虑第i种物品时的最大价值),再加上k个第i种物品的价值k*v[i]

这种方法遍历了物品的每种可能性,确保找到在不超过背包容量的情况下,物品的最大总价值。通过更新dp[i][j],最终dp[n][m]存储的就是在考虑所有n种物品,且背包容量为m时的最大价值。

二进制优化(超出内存限制)

下面是代码的详细注释,解释了每一部分的作用和实现的功能:

#include<bits/stdc++.h>
using namespace std;// 定义最大的数组范围
const int z=8000;
// 定义价值和重量数组
int v[z],w[z];
// 定义动态规划的数组,dp[i][j]表示前i件物品在不超过j体积时的最大价值
int dp[z][z];int main()
{// n 表示物品种类数,m 表示背包的容量int n,m;cin>>n>>m;// pos 用于记录当前拆分后的物品的个数int pos=0;// 遍历每种物品for(int i=1;i<=n;i++){// a 表示每件物品的体积,b 表示每件物品的价值,s 表示物品的数量int a,b,s;cin>>a>>b>>s;int k=1; // k 用于表示当前拆分的物品件数,初始为1,即2^0// 二进制拆分物品while(k<=s){// 拆分物品加入数组,体积和价值都按照拆分的件数k倍增w[++pos]=a*k;v[pos]=b*k;// 减去已经拆分的物品数量s-=k;// 更新 k 为下一次拆分的件数,即翻倍k*=2;}// 如果还有剩余未拆尽的物品,则继续拆分剩下的if(s>0){w[++pos]=a*s;v[pos]=b*s;}}// 动态规划处理每个物品for(int i=1;i<=pos;i++){// 遍历背包的容量for(int j=0;j<=m;j++){// 不选第i件物品的情况,价值等同于没有第i件物品时的价值dp[i][j]=dp[i-1][j];// 选第i件物品的情况,只有当背包容量足够时考虑if(j>=w[i])// 选和不选的最大值dp[i][j]=max(dp[i][j], dp[i-1][j-w[i]] + v[i]);}}// 输出所有物品处理完后,背包容量为m时的最大价值cout<<dp[pos][m];return 0;
}

这段代码的主要目的是通过二进制拆分将多重背包问题转化为01背包问题,并利用动态规划求解。二进制拆分是一种优化手段,它通过拆分物品数量来减少状态转移的次数,这在处理多重背包问题时非常有效。在动态规划阶段,由于物品已经被拆分,代码实际上是在按照01背包问题的方式处理每件物品。最后通过动态规划数组 dp 来得出最大价值。


http://www.ppmy.cn/news/1408940.html

相关文章

【MySQL核心SQL】

MySQL核心SQL 结构化查询语句SQL SQL是结构化查询语言&#xff08;Structure Query Language&#xff09;&#xff0c;它是关系型数据库的通用语言。 SQL主要可以划分为以下 3 个类别&#xff1a; DDL&#xff08;Data Definition Languages&#xff09;语句数据定义语言&am…

如何在Python中创建和使用全局变量和局部变量?

如何在Python中创建和使用全局变量和局部变量&#xff1f; 在Python中&#xff0c;变量根据它们被定义和使用的位置可以分为全局变量和局部变量。全局变量定义在函数或类的外部&#xff0c;而局部变量定义在函数或方法的内部。全局变量在整个程序运行期间都是可见的&#xff0…

(二)小案例银行家应用程序-创建DOM元素

● 上图的数据很明显是从我们账户数组中拿到了&#xff0c;我们刚刚学习了forEach&#xff0c;所以我们使用forEach来创建我们的DOM元素&#xff1b; const displayMovements function (movements) {movements.forEach((mov, i) > {const type mov > 0 ? deposit : w…

福州装修答疑 | 飘窗能不能砸掉?福州中宅装饰,福州装修

装修中的飘窗是一种常见的装饰元素&#xff0c;它不仅可以增加室内的采光和通风效果&#xff0c;还能为居室增添一份雅致和温馨。然而&#xff0c;很多业主在装修中都会遇到一个共同的问题&#xff1a;装修中的飘窗到底能不能砸&#xff1f;什么情况下可以砸&#xff1f;什么情…

【PostgreSQL】技术传承:使用Docker快速部署PostgreSQL数据库

前言 PostgreSQL的重要贡献者Simon Riggs因一起坠机事故不幸离世。Simon Riggs是英国著名的软件与服务领导者&#xff0c;也是PostgreSQL的主要开发者和贡献者。事故发生在英国当地时间3月26日13:41分&#xff0c;当时他驾驶的私人通用航空Cirrus SR22飞机在英国达克斯福德机场…

DOTS:Burst

目录 一&#xff1a;简介 1.1 Getting started 1.2 C# language support 1.2.1 HPC# overview 1.2.1.1 Exception expressions 1.2.1.2 Foreach and While 1.2.1.3 Unsupported C# features in HPC# 1.2.2 Static read-only fields and static constructor support 1.…

【QT+QGIS跨平台编译】056:【pdal_json_schema+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、pdal_json_schema介绍二、pdal下载三、文件分析四、pro文件五、编译实践一、pdal_json_schema介绍 pdal_json_schema 是与 PDAL(Point Data Abstraction Library)相关的 JSON 模式文件。PDAL 是一个用于处理和分析点云数据的开源库。JSON 模式…

【攻防世界】FlatScience

dirsearch 扫描发现四个文件 在login.php 中发现 输入 http://61.147.171.105:61912/login.php/?debug 发现源码 <?php if(isset($_POST[usr]) && isset($_POST[pw])){$user $_POST[usr];$pass $_POST[pw];$db new SQLite3(../fancy.db);$res $db->query(…