动态规划3—01背包梳理

一:问题解析

有一个容量为W的背包,总共有N个物品,每个物品有两个属性,重量w[i[和价值v[i],需要选择一些物品放入背包,每个物品只能选择一次,使得在不超过背包容量的情况下,物品的总价值最大;

与完全背包的不同:每个物品只能选择一次;

二:二维dp数组实现

思路:

1.定义二维数组dp[i][j]:

i表示在前i个物品中选择,j表示此时背包的容量为j,dp[i][j]表示此状态下,背包能获得的最大价值;

2.构建状态转移方程:

1):无法选择第i个物品(背包剩余容量不足(j<w[i])):

                     当前状态为:比较i个物品,背包容量为j;因为不选择该物品,所以当前状态最大价值等于上一个状态的最大价值;而上一个状态比当前状态物品少一个(i-1),容量相同(j),所以为dp[i][j]=dp[i-1][j];

(2):可以选第i个物品:如果不选:(同上)dp[i][j]=dp[i-1][j];

                     如果选:当前状态为:比较i个物品,背包容量为j;因为选择该物品,所以当前状态的最大价值等于上一个状态的最大价值,加上第i个物品的价值(v[i]);而上一个状态比当前状态物品少一个(i-1),背包容量为选择后的剩余容量(j-w[i]),所以dp[i][j]=dp[i-1][j-w[i]]+v[i];

(注意是j-w[i]不是j+w[i]啊啊啊啊!!!!!!!!!!!!!!!!!!)     

然后 用max函数判断哪个情况的价值更大;

则得到状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);

3.初始化:

在没有任何物品时(i=0),与背包容量为空(j=0)时,价值为0;

注:dp,w,v三个数组大小都开为n+1,这样可以直接用dp[0]来表示背包容量为0的最大价值,不用再对边界特殊处理,遍历时直接从i=1,j=1开始即可!

代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int N = 0, W = 0;
	cin >> N >> W;
	// n:物品数量 w:背包容量
	vector<int> v(N + 1, 0), w(N + 1, 0);
	// v:物品价值 w:物品重量
	for (int i = 1; i <= N; i++)
	{
		cin >> v[i] >> w[i];
	}
	vector<vector<int>> dp(N + 1, vector<int>(W + 1, 0));
	for(int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= W; j++)
		{
			if (j >= w[i])
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
			else
				dp[i][j] = dp[i - 1][j];
		}
	}
	cout << dp[N][W] << endl;
	return 0;
}

三:一维dp数组优化:

优点:节省空间;

思路:

1:构建一维数组dp[j]:j:背包剩余容量;dp[j]:此状态最大价值;

2:双层for循环:第一层循环遍历每一个物品,第二层倒序遍历背包容量;

3:状态转移方程:如果不选择该物体:与遍历到该物体之前的最大价值相同:dp[j]=dp[j];

                                如果选择该物体:当前最大价值等于选择该物体前,背包容量减少(j-w[i])时的最大价值相同;

所以:dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

重点:倒序遍历,确保每个物体只选择一次;

因为j-w[i]可能会与正在判断或已经判断的j值相同,导致dp[j]重复更新,即物品被重复选择

代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int N = 0, W = 0;
	cin >> N >> W;
	// n:物品数量 w:背包容量
	vector<int> v(N + 1, 0), w(N + 1, 0);
	// v:物品价值 w:物品重量
	for (int i = 1; i <= N; i++)
	{
		cin >> v[i] >> w[i];
	}
	vector<int> dp(W + 1, 0);
	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;
}

附题:P1048 [NOIP 2005 普及组] 采药  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值