问题建模:
给定一个大小为 j 的背包,现在有一堆商品:每个商品的size 和 value
现在需要给背包里装东西: 目的是:装的东西的价值要尽可能最大。
现在问你,怎么装?
问题 分析:
这种问题:乍一看好像很棘手,size 大的value 可能很小。却大大浪费了空间。
细细分析: 其实是一种动态选择问题:即动态规划:
核心:
每次遇到一个商品 ,经过衡量选择: 放入? 不放入?背包
不放入:肯定是由于空间导致
放入: 1 空间充足,直接放入,
2 空间不充足,需要衡量是否要取出一些,再放入该商品。(衡量标准:保证value最大)
那么 状态转移方程就来了:
F(i, j) 表示 当前value —>很明显,用二维数组存储的 value
因为数组下标从0开始:所以用F(i-1, j-1)来表示
i: 表示第 i 个商品
j: 表示当前背包空间大小为 j
F(i-1, j-1) = max (F(i-1, j-1), F(i-1, j- A[i-1]) + V[ i-1])//V 代表value
一目了然: 计算时开辟二维数组, 没来到一个商品前进行空间变化的各种衡量 。
最终得到数组的最后一个元素就是最大的。 (类似于动态贪心)。 DP是贪心的father·~
//A :商品空间大小 V:商品价值大小
int backPack(int m, vector<int>&A, vector<int> & V)
{
int n =A.size();//n:商品个数 m:背包大小
vector<vector<int>> maxValue(n+1,vector<int>(m+1,0));
for(int i=1; i<=n; ++i)//第i 个商品品
{
for(int j=1; j<=m; ++j)//有j个空间
{
//因为我们下标从1开始,所以实际下表应该从i-1开始
//判断当前商品空间是否大于总空间
if(A[i-1] > j)//当前商品空间大于背包空间了 返回上一个商品对应空间的最大价值
maxValue[i][j] = maxValue[i-1][j];
else//否则,: 1 不腾空间,不放 2 先腾空间,再放入
//maxValue[i-1][j]:剩余不腾空间
maxValue[i][j] = max(maxValue[i-1][j], maxValue[i-1][j-A[i-1]] + V[i-1]);
}
}
return maxValue[n][m];//返回商品n,空间j时对应的最大value
//注意:这个n并不代表n个商品都选选取了,只是说明,选择时来到过n
//同理,m也不是说m个空间全装满了,只是说明计算到了m这个地方的最大value
}
优化
注意分析 : 当我们计算时,只需要前一行的值,其它行的值都不用了,
那么我们可以优化一下: 只申请一维数组:
注意这行代码:⚠️
maxValue[j] = max(maxValue[j], maxValue[j-A[i-1]] + V[i-1]);
一维数组时:这里存在更新问题 : 当前商品的选择与否,受到上一行商品对应数据的影响,而我们只有一行,那么怎么体现上一行呢?
上一 行的数据其实就是没有更新之前的数据,我们正向走的话,ke neng会导致将本行数据当作上一行了,
解决策略: 反向走,前边的数据肯定没被更新
//A :商品空间大小 V:商品价值大小
int backPack(int m, vector<int>&A, vector<int> & V)
{
int n =A.size();//n:商品个数 m:背包大小
vector<int> maxValue(m+1,0));
for(int i=1; i<=n; ++i)//第i 个商品品
for(int j=m; j>=V[i]; --j)//有j个空间
maxValue[j] = max(maxValue[j], maxValue[j-A[i-1]] + V[i-1]);
return maxValue[m];//返回最大空间时的value
}