题目描述
求解 01 背包问题
题目描述
实现一个算法求解 01 背包问题。背包问题的介绍如下:
已知一个容量为 totalweight的背包,有不同重量不同价值的物品,问怎样在背包容量限制下达到利益最大化。
01 背包问题要求每个物品只有一个,可以选择放入或者不放入背包。
输入描述
第一行为两个数字 totalweight、N,均不超过 1000。
totalweight 含义见题干,N 为物品数量。
接下来 N 行,每行两个数字 Wi 、Vi,均不超过 1000。含义见题干。
输出描述
输出一行,为在背包容量限制下的最大化利益。
输入输出样例
示例
输入
8 5
3 4
5 5
1 2
2 1
2 3
输出
10
运行限制
最大运行时间:1s
最大运行内存: 256M
本题目代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int totalweight, N;
cin >> totalweight >> N;
vector<int> weights(N), values(N);
for (int i = 0; i < N; ++i) {
cin >> weights[i] >> values[i];
}
// 创建一个二维DP表,dp[i][j]表示从前i个物品中选择,在容量为j的背包中能达到的最大价值
vector<vector<int>> dp(N + 1, vector<int>(totalweight + 1, 0));
// 填充DP表
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= totalweight; ++j) {
if (weights[i - 1] <= j) {
// 如果当前物品可以放入背包,则比较放入和不放入两种情况下的最大价值
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);
} else {
// 如果当前物品太重,不能放入背包,则继承前一个物品的结果
dp[i][j] = dp[i - 1][j];
}
}
}
// 输出最大价值,即dp[N][totalweight]
cout << dp[N][totalweight] << endl;
return 0;
}
本题目代码解释
这个程序首先读取背包的总容量 totalweight
和物品的数量 N
,然后读取每个物品的重量 Wi
和价值 Vi
。接下来,它使用一个二维动态规划表 dp
来存储子问题的解,其中 dp[i][j]
表示从前 i
个物品中选择,在容量为 j
的背包中能达到的最大价值。
通过两层循环,程序填充了 dp
表。外层循环遍历每个物品,内层循环遍历所有可能的背包容量。对于每个物品和每个容量,程序检查是否可以将该物品放入背包中(即检查 weights[i - 1] <= j
),如果可以,则比较放入和不放入该物品时的最大价值,并更新 dp[i][j]
。如果物品太重无法放入背包,则继承前一个物品的结果。
最后,程序输出 dp[N][totalweight]
,即在所有物品和背包容量限制下的最大化利益。
考研中的01背包问题
给定:
一个背包,其最大承重为 c。
一组物品,每个物品 i 有其重量 w[i] 和价值 v[i]。
每个物品只能选择装入或不装入背包。
目标:
在不超过背包最大承重 W 的前提下,选择装入背包的物品,使得背包中物品的总价值最大。
DP递推公式
m[i][j]
表示的是在考虑前i
个物品时,背包容量为j
的情况下能达到的最大价值。
m[i][j]=0 i=0&j=0
m[i][j]=m[i-1][j] w[i]>j
m[i][j]=max(m[i−1][j],m[i−1][j−w[i−1]]+v[i−1]) w[i]<=j
这里,m[i-1][j]
表示不选择第i
个物品时,背包容量为j
的最大价值;m[i-1][j-w[i-1]] + v[i-1]
表示选择第i
个物品时,背包容量为j
的最大价值(这里假设第i
个物品可以被放入背包,即j-w[i-1]
非负)。我们通过比较这两种情况来决定是否选择第i
个物品。
01背包代码
const int maxSize = 100;
int pack(int n, int c, int v[], int w[], int m[][maxSize]) {
for (int i = 1; i <= n; i++)
m[i][0] = 0;
for (int i = 0; i <= c; i++)
m[0][i] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= c; j++) {
if (w[i - 1] > j)
m[i][j] = m[i - 1][j];
else
m[i][j] = std::max(m[i - 1][j], m[i - 1][j - w[i - 1]] + v[i - 1]);
}
return 0;
}
void traceBack(int n, int c, int w[], int m[][maxSize], int x[]) {
for (int i = n; i >= 1; --i)
if (m[i][c] == m[i - 1][c])
x[i] = 0;
else {
x[i] = 1;
c -= w[i];
}
}
01背包DP表格
假设我们有4个物品,背包容量为5,物品的重量和价值分别如下:
- 物品1: 重量1,价值2
- 物品2: 重量2,价值3
- 物品3: 重量3,价值4
- 物品4: 重量4,价值5
0 | 1 | 2 | 3 | 4 | 5 | ||
0 | 0 | 0 | 0 | 0 | 0 | 0 | |
1 | 0 | 2 | 2 | 2 | 2 | 2 | (选择物品1或不选) |
2 | 0 | 2 | 3 | 3 | 5 | 5 | (选择物品2或之前的选择) |
3 | 0 | 2 | 3 | 4 | 5 | 7 | (选择物品3或之前的选择) |
4 | 0 | 2 | 3 | 4 | 5 | 9 | (选择物品4或之前的选择) |
表格的每一行代表考虑了某个数量的物品时的状态,每一列代表背包的不同容量。m[i][j]
的值表示在考虑前i
个物品且背包容量为j
时能达到的最大价值。
- 第一行(
i=0
)和第一列(j=0
)都初始化为0,因为没有任何物品或背包容量为0时,价值自然是0。 - 对于每一行(除了第一行),我们从左到右填充值。如果当前物品的重量大于背包的当前容量(
w[i-1] > j
),则不选择该物品,价值与前一行相同(m[i][j] = m[i-1][j]
)。 - 否则,我们比较两种情况:不选择当前物品(
m[i-1][j]
)和选择当前物品(m[i-1][j-w[i-1]] + v[i-1]
),并选择其中的较大值。
在上面的表格中,你可以看到:
- 在考虑物品1时,只有当背包容量至少为1时,我们才能选择物品1(价值为2)。
- 在考虑物品2时,如果背包容量至少为2,我们可以选择物品2(价值为3),或者如果背包容量不足以容纳物品2但足以容纳物品1,则选择物品1。
- 类似地,对于物品3和物品4,我们根据背包的当前容量和物品的重量/价值来决定是否选择它们。
最终,m[4][5]
的值(即表格的右下角)给出了在背包容量为5时,选择所有物品(或其中的某些物品)能得到的最大价值,这里是9。