简介:
背包问题可以描述为:给定⼀组物品,每种物品都有⾃⼰的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最⾼。
我们看一个01背包的例子:
例如:有一个背包体积为5
我们有非常多的选择方案,
但价值最多的方案只有选择体积为4的2号物品才可以得到最大价值,虽然背包没有装满。
那么01背包的意思是什么呢?
所有的物品只有1份,取走就是0份,所以理解为01背包
。
反之,若是所有物品都是无限份,那么就是完全背包。
例题:
题目链接:01背包。
思路:
第一问:
按照动态规划的套路来走,分为
- 确定状态表示:
- 确定状态转移方程:
- 初始化:
- 确定填表顺序:
- 返回答案
我们先尝试使用一般的思路进行解决,以某一位置为结尾进行分析。
dp[i]:在前i个物品中挑选,所有的选法中最大的价值。
以上边的状态表示分析转移方程是得不到的。
原因在于:
所以我们通常面对01背包问题会有一个比较通用的状态表示:
dp[i][j] 表⽰:从前 i 个物品中挑选,总体积不超过 j ,所有的选法中,能挑选出来
的最⼤价值。
分析状态方程:
初始化:
我们多加⼀⾏,⽅便我们的初始化,此时仅需将第⼀⾏初始化为 0 即可。
因为根据我们的状态转移方程,i == 0就是没有物品时,理所当然价值为0。
那么需不需要多加一列呢?为了方便我们可以多加一列,避免了下标的不对应。
填表顺序:
分析一下状态方程即可得到大方向是由上到下
即可。
第二问:
与第一问几乎一样,但需要改几个点。
注意:由于dp[i][j]可能存在非法的情况,也就是并没有体积==j时的情况,我们将这种情况设置为-1。
为什么不设置为0?状态方程那会有分析。
dp[i][j] 表⽰:从前 i 个物品中挑选,总体积== j ,所有的选法中,能挑选出来的最⼤价值。
分析状态方程:
初始化:
我们多加⼀列,由状态表示得:无论由几个物品,但是我们都需要==j,因此都填为0。
也要多加一行,也是由状态表示得:从 j > 0 开始,由于物品始终为0的缘故,所以都是非法情况,故都为-1。
填表顺序:
分析一下状态方程即可得到大方向是由上到下
即可。
代码:
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int main()
{
// 第一问
// 初始化dp表与w和v数组
int n, V;
cin >> n >> V;
vector<int> w(n + 1);
vector<int> v(n + 1);
vector<vector<int>> dp1(n + 1, vector<int>(V + 1));
for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
// 填表
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= V; j++)
{
dp1[i][j] = dp1[i-1][j];
if (j >= v[i])
{
dp1[i][j] = max(dp1[i][j], w[i] + dp1[i-1][j-v[i]]);
}
}
}
cout << dp1[n][V] << endl;
// 第二问
vector<vector<int>> dp2(n + 1, vector<int>(V + 1));
for (int i = 1; i <= V; i++) dp2[0][i] = -1;
// 填表
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= V; j++)
{
dp2[i][j] = dp2[i-1][j];
if (j >= v[i] && dp2[i-1][j-v[i]] != -1)
{
dp2[i][j] = max(dp2[i][j], w[i] + dp2[i-1][j-v[i]]);
}
}
}
cout << (dp2[n][V] == -1 ? 0 : dp2[n][V]);
return 0;
}
优化:
利用滚动数组来进行空间优化。
当我们填表的位置为红圈时,对钩为我们可能会用到的位置,因此,我们可以使用一行数组进行优化空间,每次覆盖式的填充。
注意:
这样我们就需要从右往左进行优化,避免造成覆盖。
另外,优化的代码我们可以直接在源代码中进行修改,只需将dp表中的横坐标全部删掉即可!
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int main()
{
// 第一问
// 初始化dp表与w和v数组
int n, V;
cin >> n >> V;
vector<int> w(n + 1);
vector<int> v(n + 1);
vector<int> dp1((V + 1));
for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
// 填表
for (int i = 1; i <= n; i++)
for (int j = V; j >= v[i]; j--)
dp1[j] = max(dp1[j], w[i] + dp1[j-v[i]]);
cout << dp1[V] << endl;
// 第二问
vector<int> dp2(V + 1);
for (int i = 1; i <= V; i++) dp2[i] = -1;
// 填表
for (int i = 1; i <= n; i++)
for (int j = V; j >= v[i]; j--)
if (dp2[j-v[i]] != -1)
dp2[j] = max(dp2[j], w[i] + dp2[j-v[i]]);
cout << (dp2[V] == -1 ? 0 : dp2[V]);
return 0;
}
完~