0
点赞
收藏
分享

微信扫一扫

01背包基本问题

猎书客er 2022-04-06 阅读 44
动态规划

假设我们面临一个问题

题目

题目描述

输入格式

输出格式

输入样例

输出样例

数据范围

分析

假设有三个物品:吉他(价值15 重量1),笔记本(价值20 重量3),音响(价值30 重量4)

 并且背包容量是4

贪心算法

按照贪心的想法,要获得最大价值应该先拿价值最大的物品,也就是音响,价值30,但是如果这样一拿,背包就直接装满了,也就是说最终得到价值30的东西,但我们可以很快构造出价值35的组合:吉他+笔记本。所以,贪心算法是难以解决此问题的。

那有没有一种算法能快速解决此问题呢?

动态规划算法

我们可以创建一个数组F

//f[i][j]表示前i个物品,背包容量不超过j时的最大价值

用图像说明的话就是:

 那接下来的任务就是如何填充此表格了

首先看第一行,我们知道吉他的重量是1,价值是15,所以选取吉他并且重量不超过1的最大价值就是吉他的价值15。接下来一想,显然,这一行都是15

 再来看第二行,我们知道笔记本的重量是3,价值是20,那么看这一行的第一格,它的意义是前2个物品中,背包容量不超过1的最大价值。因为容量限制在1以内,所以只能是一个吉他,价值15

 第二格,表示前2个物品中,背包容量不超过2的最大价值。这时候我们有两个选择:

  1. 不选择笔记本,最大价值就是前1个物品中背包容量不超过2的最大价值,就是15
  2. 选择笔记本,最大价值就是前1个物品中(因为已经选了笔记本)背包容量不超过2-笔记本重量的最大价值

但是笔记本的重量是大于2的,所以只能选择方案1,价值还是15

接下来看第三格,同样,两个选择:

  1.  不选择笔记本,最大价值就是前1个物品中背包容量不超过3的最大价值,就是15
  2. 选择笔记本,最大价值就是前1个物品中(因为已经选了笔记本)背包容量不超过3-笔记本重量的最大价值

这是方案2就可行,因为笔记本重量=3,所以方案2所得的价值是20+前0个……,也就是20嘛

欸,那这时,20 > 15,所以这时方案2会优于方案1,选择方案2

因此,我们可以得到递推式

f[i][j] = max(f[i - 1][j],f[i - 1][j - w[i]] + v[i]);

其中,f[i - 1][j]为不选第i件物品的最大价值,f[i - 1][j - w[i]] + v[i]为选第i件物品的最大价值

我们就是在这两个值中取较大的

所以表格剩下的也就按照这个递推式来进行即可

 

最终答案是表格的右下角也就是 f[n][m],最大价值35

但在递推式中,需要注意的是,如果j < w[i]时,就只能是 f[i - 1][j]

我们还需要考虑初始值,其实这很容易,如果这一上来体积就大于背包容量了,那所拿的价值就是0,所以 f 数组中每一项初始值为0

此时,算法原理已经讲完了,而代码实现难度并不高,我们直接来看代码

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int w[1010],v[1010];
//w[i]表示第i件物品的重量,v[i]表示第i件物品的价值
int f[1010][1010];
//f[i][j]表示前i个物品重量不超过j的最大价值

int main() 
{
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++)
	{
		cin >> w[i] >> v[i];
	}
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			f[i][j] = f[i - 1][j];
			if(j - w[i] >= 0)
				f[i][j] = max(f[i - 1][j],f[i - 1][j - w[i]] + v[i]);
		}
	}
	cout << f[n][m];
    return 0;
}

最后,如果你没有理解这个算法的话,我强烈建议你手动模拟一下整个过程,这样,你的理解程度会更深一些。

举报

相关推荐

0 条评论