经典的0-1背包问题

经典的0-1背包问题-滚动数组优化

背包问题是一个经典的问题,除此之外还有其扩展的变形。
下面,我们把背包问题仅仅当做一个问题来解决,而不太多的去思考动态规划这个笼统的概念。
下面我会把这个经典的最原始的背包问题的算法思路详细的表述出来,并从二维数组到一维数组优化(空间优化)

注:(二维数组到一维数组的空间优化方案简称为滚动数组。)

注:(首先,要大致了解解决背包问题的动态规划思想,但是懂得解决背包问题的动态规划思想未必会举一反三,在解决背包问题的时候,我避免了去思考动态规划这个笼统的概念,以防止搞不清楚动态规划到底是什么。)

算法思路

这是物品的重量和价值。

物品重量w价值v
a26
b23
c65
d54
e46

分析:什么情况下给定的背包容量会装入最大价值?
对物品一个个进行选择。

选择012345678910
对第1个物品选择00666666666
对第2个物品选择00669999999
对第3个物品选择00669999111114
对第4个物品选择006699910111314
对第5个物品选择0066991212151515

上述表格中的数据的含义:当对第i个物品进行选择,背包容量为j时所容纳的最大价值。
dp[i][j]代表对第i个物品进行选择,背包容量为j时所容纳的最大价值。
dp[i-1][j]代表对第i-1个物品进行选择,背包容量为j时所容纳的最大价值。
dp[i-1][j-w[i]]代表对第ii物品进行选择,背包容量为j-w[i]时所容纳的最大价值。
而通过上面表格的分析,可得出一个状态方程:dp[i][j] = max{dp[i-1][j], v[i] + dp[i-1][j-w[i] }

由分析发现,当准备更新dp[i][j]的值的时候,dp[i-1][j]和dp[i-1][j-w[i]]的值会被取出使用,之后会填入dp[i][j]中,当对第i个物品选择结束后,第i-1个物品的空间就没用了,所以如果将状态方程转化为:dp[j] = max{dp[j], v[i] + dp[j-w[i]]} 就实现了空间的优化。
注(如果没看懂我上面那段话,就不要懒于分析了,动手分析一下,画个如上图的二维表格,一步一步进行分析。)

源代码

一维滚动数组优化。
背包容量的for循环倒着。

#include <iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int dp[50001],w[10001],v[10001],W,n;
int max(int a,int b)
{
	return a>b?a:b;
}
int main()
{

	memset(dp, 0, sizeof(dp));
	scanf("%d%d", &n,&W);
	for (int i = 1; i <= n; i++)
		scanf("%d%d", &w[i], &v[i]);
	//对第i个物品进行选择,从大于第i个物品的背包承重量开始,进行计算。之所以使for (int j = W; j >=w[i]; j--) j倒着来,是因为一维数组要进行数据覆盖。
	for (int i = 1; i <=n; i++)
	for (int j = W; j >=w[i]; j--)
		dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
	cout <<dp[W] << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值