01背包问题详解

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值