动态规划经典题目之01背包问题
Posted 快乐江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划经典题目之01背包问题相关的知识,希望对你有一定的参考价值。
问题描述
给你一个可装载重量为 W
的背包和 N
个物品,每个物品有重量和价值两个属性。其中第 i
个物品的重量为 wt[i]
,价值为 val[i]
,现在让你用这个背包装物品,最多能装的价值是多少?
解题思路
01背包问题是一个非常经典的动态规划问题,可以很好的入门动态规划,与之相关的变形题目也是非常的多
(1)状态和选择是什么?
- 状态:由于是不断将物品放入状态,所以变化的量有两个——“背包容量”和可选择的物品
- 选择:选择就是要么装进去要么不装进(也就是0和1)
因此选择导致了状态变化,如果选择装入将导致背包容量减少以及可选择的物品减少
(2)dp数组如何定义
接着要根据状态和选择来定义dp数组。dp数组完成的就是动态转移,dp数组的值一定是价值,这是毋庸置疑的。动态规划框架如下
题目问的是当给定物品个数为N,背包容量为W时所能装的最大的价值数目,那么我们将dp数组也定义为dp[i][w]
(动态规划本质就在于状态的转移,就像是砌墙一样,上面的每一砖每一瓦都代表一个状态,它们的到的和下面的砖瓦是息息相关的),它表示对于前i个物品,如果给定容量为w,可以装的最大价值为dp[i][w]
后序的操作就是从最简单的情形开始,一步一步推导每个状态,最终当返回i=N
,w=W
时的结果即可。
(3)base case
base case很容易想到。最极端最简单的情况自然是i=0,此时表示不选择物品,以及w=0,此时表示背包容量为0。他们的结果均为0
(4)框架
于是完善最原始的框架如下
int dp[N+1][W+1]
dp[0][..] = 0
dp[..][0] = 0
for i in [1..N]:
for w in [1..W]:
dp[i][w] = max(
把物品 i 装进背包,
不把物品 i 装进背包
)
return dp[N][W]
动态规划最难的地方,就是动态转移,也即是如何将上面的文字表述为代码
(5)动态转移
- 如果没有把第
i
个物品放在背包:很显然,既然没有把第i个放进去,那么价值量不会增加,状态也不会变化,也即dp[i][w]==dp[i-1][w]
- 如果把第
i
个物品放入了背包:既然放入了背包,那么此状态的容量一定会减少wt[i]
,而价值则会增加val[i]
,因此dp[i][w]==dp[i-1][w-wt[i-1]]+val[i-1]
需要注意的是i
是从1开始的,因此val
和wt
的索引中i-1
表示第i
个物品。所以dp[i][w]==dp[i-1][w-wt[i-1]]+val[i-1]
表示如果把第i个物品装入了,就要寻找剩余重量w-wt[i-1]
限制下的最大价值,加上第i
个物品的价值val[i-1]
代码
int knapsack(int W, int N, vector<int>& wt, vector<int>& val) {
// vector 全填入 0,base case 已初始化
vector<vector<int>> dp(N + 1, vector<int>(W + 1, 0));
for (int i = 1; i <= N; i++) {
for (int w = 1; w <= W; w++) {
if (w - wt[i-1] < 0) {
// 当前背包容量装不下,只能选择不装入背包
dp[i][w] = dp[i - 1][w];
} else {
// 装入或者不装入背包,择优
dp[i][w] = max(dp[i - 1][w - wt[i-1]] + val[i-1],
dp[i - 1][w]);
}
}
}
return dp[N][W];
}
01背包问题相关题目
1:牛客-求正数数组的最小不可组成和
如果按照原生的背包问题可以这样理解:min
为最轻物品的质量,sum
为所有物品的总质量,假设有一个背包,其容量范围在[min,sum]
之间,还有len
件不同重量的物品、、、
也即把数组中的数据看作物品的重量,如果这些物品不能填满某个容量(范围为[min,max])的背包,就表示不能组成那个范围的数
class Solution {
public:
/**
* 正数数组中的最小不可组成和
* 输入:正数数组arr
* 返回:正数数组中的最小不可组成和
*/
int getFirstUnFormedNum(vector<int> arr, int len)
{
//范围为[min,sum];
int sum=0,min=arr[0];
int i,j;
for(int i=0;i<len;i++)
{
sum+=arr[i];
min=arr[i] < min ? arr[i] : min;
}
vector<int> dp(sum+1,0);
for(i=0;i<len;i++)
{
for(j=sum;j>=arr[i];j--)//对于背包容量小于物品的直接忽略
{
if(dp[j] < dp[j-arr[i]]+arr[i])//选上了
dp[j]=dp[j-arr[i]]+arr[i];
else//没选上
dp[j]=dp[j];
}
}
//最后只要放入的重量不是那个区间的数肯定就是所求
for(i=min;i<=sum;i++)
{
if(i!=dp[i])
return i;
}
return sum+1;
}
};
以上是关于动态规划经典题目之01背包问题的主要内容,如果未能解决你的问题,请参考以下文章