01背包
有N件物品和一个最多能装重量为W的背包。每件物品都有对应的重量和价值。求将哪些物品装入背包才能使总价值最大。
二维DP数组
1、确定DP数组
我们用二维数组来求解这个问题。DP[i][j]表示从下标为[0-i]的物品里任意取物品,放进容量为j的背包里面,价值总和为多少。(这个定义很重要)
2、确定递推公式
我们DP数组的定义是什么?DP[i][j]表示从下标为[0-i]的物品里任意取物品,放进容量为j的背包里面,价值总和为多少。那么我们怎么推出这个DP[i][j]呢?当我们拿到第i件物品的时候,我们必须要考虑放和不放的问题,如果不放,那么背包的容量以及价值就和第i-1件物品的时候一致,没有变化,也就是DP[i][j] = DP[i - 1][[j];如果放,那么背包的容量会减少至j - weight[i],我们先把物品i单独拿出来,但是在背包中给物品i留足空间,此时背包的价值就是DP[i - 1][j-weight[i]],然后我们再把物品i放进去,因为我们已经给它提前留足的空间,所以当我们再放物品i的时候,只需要考虑将物品i的价值加到背包的总价值当中,也就是DP[i - 1][j-weight[i]] + value[i]。此时我们就该考虑最初的问题,放还是不放,怎么选择?当然是哪种价值更高就选择哪种,所以最后DP[i][j] = max(DP[i - 1][j-weight[i]] + value[i], DP[i - 1][[j])
所以我们可以由两个方向推出这个DP[i][j]:
(1) DP[i- 1][j],也就是正上方
(2) DP[i - 1][j-weight[i]],也就是左上方
因此我们只需要初始化数组第一行和第一列的值,就可以根据递推公式求出整个数组的值,而最大价值自然也就在整个数组的最右下角即最后一个元素。
3、初始化数组
上面我们说了,只需要初始化数组第一行和第一列的值。第一列,当背包承受的最大重量为0的时候,最大价值是多少,不用想了,能承受最大重量为0,那什么也装不下,所以第一列的值全为0
然后就是第一行的值,第一个还是为0,已经初始化好了,然后我们假设物品1的重量为1,价值为10,那么第一行除了第一个元素就全部初始化为10
但是如果物品1的重量为2,价值为10,那么在1这个位置,我们也要初始化为0,因为根本放不进去。然后其他位置我们初始化值应该为多少呢?其实为多少都无所谓,因为我们的递推公式是会直接将这些位置的值覆盖,不会进行比较,所以你愿意设置多少都可以,我们一般默认为0。
说了这么多,应该理解我们要怎么初始化了吧,不理解也没关系,其实我们直接全部初始化为0就可以了,因为我们的递推公式DP[i][j] = max(DP[i - 1][j-weight[i]] + value[i], DP[i - 1][[j]),就算第一行全部为0,在遍历的时候也会被加上value[i]的值然后把0覆盖。那我为什么还要讲这么多,因为我们要理解这个递推公式。
4、遍历数组
当我们开始遍历数组的时候,就会有一个问题,我们是从物品开始遍历还是从背包重量开始遍历。其实都是在01背包问题中,这两种顺序都是可行的,原因就在递推公式中,留给读者自行探索。这里我们就先遍历物品,再遍历背包重量。
for (int i = 1; i <= n; i++) // n表示物品的种类
for (int j = 0; j <= t; j++) // t表示背包的最大容量
if (j >= weight[i])
DP[i][j] = max(DP[i - 1][j - weight[i]] + value[i], DP[i - 1][j]);
else
DP[i][j] = DP[i - 1][j];
5、推导DP数组
这里就不一一推导了,因为我们在前面已经分析清楚了,数组的最后一个元素就是我们要求的最大价值也就是DP[n][t],注意,我是从1号位开始存储而非0号位,读者自行推理。
代码
#include <iostream>
using namespace std;
// weight数组用来存放物品重量,value数组用来记录物品对应的价值
// 数组的大小视情况而定
int DP[105][1005], weight[105], value[105];
int main()
{
int t, n;
cin >> t >> n;
for (int i = 1; i <= n; i++)
{
cin >> weight[i] >> value[i];
}
for (int i = 1; i <= n; i++)
for (int j = 0; j <= t; j++)
if (j >= weight[i])
DP[i][j] = max(DP[i - 1][j - weight[i]] + value[i], DP[i - 1][j]);
else
DP[i][j] = DP[i - 1][j];
cout << DP[n][t];
return 0;
}