我的代码:
#include
#include
#include //包含向量库,程序中的数据结构主要使用了 vector 来存储和处理数据
using namespace std;
int main() {
int N, m;//N 是背包的容量(单位是10),m 是物品的数量
cin >> N >> m;
vector> v(m + 1, vector(3, 0));
/* 该行代码创建了一个二维 vector,
总共有 m + 1 行,每行有 3 个元素,且每个元素的初始值都为 0
vector:表示一个动态数组容器,存储整数,可以根据需要调整大小
vector>:这是一个二维容器,实际上是一个由多个 vector 组成的数组,
可以理解为一个矩阵,其中每个元素是一个 vector
m + 1:表示二维数组有 m + 1 行(第一个维度的大小)
这里 m 表示物品的数量,m + 1 是因为要包含 0 到 m 的索引,通常是为了方便处理边界条件
vector(3, 0):表示每一行是一个长度为 3 的 vector,并且每个元素的初始值都设置为 0
每一行有 3 个元素,分别用来存储一个物品的三种价格(主件价格和两个附件的价格)
v[i][0] 存储第 i 个物品的主件价格。
v[i][1] 和 v[i][2] 分别存储附件 1 和附件 2 的价格 */
vector> w(m + 1, vector(3, 0));
/* 这行代码类似于 v 的定义,创建了另一个 二维动态数组 w,
它用于存储 物品的价格和重要度的乘积,即 满意度
结构和初始化方式与 v 相同,只是它的用途不同:
w[i][0] 存储第 i 个物品主件的 价格乘以重要度(满意度)
w[i][1] 和 w[i][2] 分别存储附件 1 和附件 2 的 价格乘以重要度(满意度)*/
vector> dp(m + 1, vector(N + 1, 0));
/* 这行代码创建了一个 二维动态数组 dp,用于动态规划的计算,特别是在背包问题中用于存储最优解解
dp 数组的作用通常是保存状态,表示在考虑了前 i 个物品,并且背包容量为 j 时,所能获得的最大价值 (这里指最大满意度)
m + 1 表示有 m + 1 行
N + 1 表示每行有 N + 1 列,这里的 N 是背包容量,N + 1 是为了方便处理容量为 0 到 N 的情况
每个元素的初始值为 0,表示初始状态下的最大满意度 */
N /= 10;
//将背包容量单位化,转换成整数(这是因为输入的容量是10的倍数)
for (int i = 1; i < m + 1; i++) {
// 物品编号从1开始,q为0表示主件,为1表示第1件物品的附件
int vv, p, q;
//vv(价格),p(重要度),q(是否是附件)
cin >> vv >> p >> q;
vv /= 10; // 简化计算,加快计算速度
if (q) { // 如果q不为0,则说明第i个物品是附件
if (w[q][1]) { // 如果w[q][1]不为0,则说明不是第一个附件
v[q][2] = vv;
w[q][2] = vv * p;
} else { // 否则就是第一个附件
v[q][1] = vv;
w[q][1] = vv * p;
}
} else { // 否则,第i个物品是主件
v[i][0] = vv;
w[i][0] = vv * p;
}
/* w[q][1] 存储的是第 q 个物品的第一个附件的满意度(即附件1的价格 * 重要度)
如果 w[q][1] 不为0,说明这个附件的满意度已经被赋值,即说明附件1已经被处理过
如果 w[q][1] 的值为0,那么这个附件还没有被赋值,因此可以确定它是第一个附件,
此时就可以将它存储为附件1的价格和满意度
如果 w[q][1] 的值不为0,就说明第一个附件已经有了对应的价格和满意度,
那么当前处理的就是第二个附件(即 v[q][2] 和 w[q][2])。*/
}
for (int j = 1; j < N + 1; j++) { // 遍历背包
for (int i = 1; i < m + 1; i++) { // 遍历物品
if (v[i][0] > j) {
dp[i][j] = dp[i-1][j]; // 都不买
/* 在考虑第 i 个物品时,如果不放入第 i 个物品(即选择不放)
那么最大价值将与前 i-1 个物品、容量为 j 的情况下的最大价值相同 */
} else {
if (v[i][0] <= j) {
/* 如果主件的重量小于等于背包当前的容量 j
则考虑将主件放入背包中,更新 dp[i][j] 的值 */
dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i][0]]+w[i][0]);
/* 更新 dp[i][j] 为当前最大值,考虑两种情况:
不放入第 i 个物品主件,dp[i][j] 保持不变
放入第 i 个物品的主件,
更新背包容量为 j - v[i][0] 后的最大价值,再加上该主件的价值*/
}
if (v[i][0]+v[i][1] <= j) { // 买主件+附件1
/* 检查主件和附件的总重量 v[i][0] + v[i][1]
是否小于等于当前背包的容量 j */
dp[i][j] = max(dp[i][j], dp[i-1][j-v[i][0]-v[i][1]]+w[i][0]+w[i][1]);
/* 选择当前背包容量 j 下的最大价值,
要么是选择不放入当前物品的主件和附件,保持原值 dp[i][j],
要么是选择放入当前物品的主件和附件,更新为
dp[i-1][j-v[i][0]-v[i][1]]+w[i][0]+w[i][1] */
}
if (v[i][0]+v[i][2] <= j) { // 买主件+附件2
/* 检查主件和附件的总重量 v[i][0] + v[i][2]
是否小于等于当前背包的容量 j */
dp[i][j] = max(dp[i][j], dp[i-1][j-v[i][0]-v[i][2]]+w[i][0]+w[i][2]);
/* 选择当前背包容量 j 下的最大价值,
要么是选择不放入当前物品的主件和附件,保持原值 dp[i][j],
要么是选择放入当前物品的主件和附件,更新为
dp[i-1][j-v[i][0]-v[i][2]]+w[i][0]+w[i][2] */
}
if (v[i][0]+v[i][1]+v[i][2] <= j) { // 买主件+附件1+附件2
/* 检查主件和附件的总重量 v[i][0]+v[i][1]+v[i][2]
是否小于等于当前背包的容量 j */
dp[i][j] = max(dp[i][j], dp[i-1][j-v[i][0]-v[i][1]-v[i][2]]+w[i][0]+w[i][1]+w[i][2]);
/* 选择当前背包容量 j 下的最大价值,
要么是选择不放入当前物品的主件和附件,保持原值 dp[i][j],
要么是选择放入当前物品的主件和附件,更新为
dp[i-1][j-v[i][0]-v[i][1]-v[i][2]]+w[i][0]+w[i][1]+w[i][2] */
}
}
}
}
cout << dp[m][N] * 10 << endl;
/* 由于输入的背包容量 N 和物品的价格都是按 10 的倍数给出的,
因此在代码中已经将它们除以 10 以简化计算
这里的 * 10 是为了将最终的结果还原成以 10 为单位的满意度值 */
return 0;
}
思路解析:
输入:
N
:背包容量(单位是10),需要进行转换为整数m
:物品数量,每个物品可能有多个附件数据存储结构:
v
:存储每个物品的价格;v[i][0]
存储第 i
个物品主件的价格,v[i][1]
和 v[i][2]
分别存储附件1和附件2的价格w
:存储价格与重要度的乘积,表示满意度;w[i][0]
存储第 i
个物品主件的满意度,w[i][1]
和 w[i][2]
存储附件1和附件2的满意度dp
:动态规划数组,dp[i][j]
表示前 i
个物品,背包容量为 j
时能够获得的最大满意度动态规划:
dp
,其中 dp[i][j]
表示在考虑前 i
个物品时,背包容量为 j
的情况下的最大满意度dp[i][j]
为当前最大满意度,即选择不放入当前物品或放入当前物品的最佳方案输出:
代码细节:
物品与附件处理:
q != 0
),通过 q
确定它属于哪个主件;附件的价格和满意度分别存储到 v[q][1]
和 w[q][1]
(附件1),v[q][2]
和 w[q][2]
(附件2)。动态规划转移:
max(dp[i-1][j], dp[i-1][j-v[i][0]] + w[i][0])
来考虑放入或不放入物品主件dp[i][j]
,分别考虑放入附件1、附件2或者同时放入主件和附件代码的时间复杂度:
O(m * N)
,其中 m
为物品数量,N
为背包容量0-1 背包问题是一个经典的动态规划问题
问题描述
给定一个背包,背包有一定的容量 C
。同时有 n
个物品,每个物品有两个属性:重量 w[i]
和价值 v[i];
每个物品只能选择一次,即要么放入背包,要么不放入背包;目标是选择一些物品放入背包,使得背包中的物品总重量不超过容量 C
,并且物品的总价值最大
输入
n
:物品的数量C
:背包的容量w[i]
:第 i
个物品的重量v[i]
:第 i
个物品的价值输出
动态规划解法
为了求解该问题,通常使用动态规划(DP)方法;定义一个二维的 dp
数组,其中 dp[i][j]
表示在前 i
个物品中,容量为 j
的背包能够容纳的最大价值
状态转移方程
dp[i][j] = dp[i-1][j]
:表示不选择第 i
个物品的情况,即价值等于不放这个物品时的最大价值dp[i][j] = max(dp[i][j], dp[i-1][j-w[i]] + v[i])
:表示选择第 i
个物品的情况,前提是 j
大于等于 w[i]
(即当前背包容量足够容纳该物品)最终,dp[n][C]
就是我们要求的最大价值
代码实现
#include
#include
#include
using namespace std;
int main() {
int n, C;
cin >> n >> C; // 输入物品的数量和背包的容量
vector w(n + 1), v(n + 1); // w[i] 是第i个物品的重量,v[i] 是第i个物品的价值
// 输入每个物品的重量和价值
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
}
// dp[i][j] 表示前i个物品,背包容量为j时的最大价值
vector> dp(n + 1, vector(C + 1, 0));
// 动态规划填充 dp 数组
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= C; j++) {
// 不选第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]);
}
}
}
// 输出最大价值
cout << dp[n][C] << endl;
return 0;
}
二维数组是数组的一种扩展,它包含多个一维数组;可以将其看作一个表格,包含行和列;二维数组的元素可以通过行和列的索引来访问
二维数组的定义
在 C++ 中,定义一个二维数组的方式是:
type array_name[rows][cols];
type
:数组中元素的类型(如 int
、float
、char
等)rows
:二维数组的行数cols
:二维数组的列数访问二维数组元素
二维数组的元素通过 [row][col]
的方式来访问,其中 row
是行索引,col
是列索引,注意数组的索引从 0
开始
arr[0][0] = 1; // 第一行第一列的元素赋值为 1
arr[1][2] = 5; // 第二行第三列的元素赋值为 5
初始化二维数组
可以在定义数组时直接初始化:
静态初始化
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
上述代码定义了一个 3x4
的数组,并为每个元素赋了初值
部分初始化
如果没有初始化所有的元素,其他未初始化的元素会默认初始化为 0
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6}
};
动态分配二维数组
可以使用动态内存分配来创建二维数组,这种方法在运行时动态地为数组分配内存
int **arr;
int rows = 3, cols = 4;
// 动态分配二维数组
arr = new int*[rows]; // 为行分配内存
for (int i = 0; i < rows; i++) {
arr[i] = new int[cols]; // 为每一行分配内存
}
// 使用二维数组
arr[0][0] = 1;
arr[1][2] = 5;
arr[2][3] = 10;
// 释放内存
for (int i = 0; i < rows; i++) {
delete[] arr[i]; // 释放每一行的内存
}
delete[] arr; // 释放二维数组的内存
遍历二维数组
可以使用嵌套循环来遍历二维数组;外层循环用于遍历行,内层循环用于遍历列
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << arr[i][j] << " "; // 输出每个元素
}
cout << endl; // 换行
}