01背包问题(DP求解)
2021-12-21 本文已影响0人
Epimenides
题目介绍
有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。
0-1背包问题
0-1背包问题
是较为简单的动态规划问题,也是其他背包问题的基础。
动态规划是不断决策求最优解的过程,0-1背包问题
即是不断对第i个物品做出决策,0-1
就是代表选择或者不选择两种决定。

相关概念
-
f[i][j]
:代表在前i件物品中选,总体积小于j的集合的最大值。-
背包问题可以被视为选择问题,选择问题的状态表示都是相似的,第一维是选择前i个物体,后面一维表示限制。
-
当前的状态依赖于之前的状态,从
f[0][0] = 0
开始决策,有N件商品,则需要进行N次0-1决策
。状态f[i][j]
就是由之前的状态不断更新而来。
-
-
遇到(
j < v[i]
)背包容量不够的情况时,前i
件物品最优解即为i-1
件物品的最优解f[i][j] = f[i - 1][j];
-
当背包容量够时,可以选,那么就要从选择与不选择第i件物品的最优解中取最优解。
f[i][j] = f[i - 1][j - v[i]] + w[i]; // 选择第i件物品 f[i][j] = f[i - 1][j] // 不选择第i件物品 f[i][j] = max(f[i - 1][j - v[i]] + w[i], f[i - 1][j]);
二维数组解题
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int n, m;
int f[N][N];
int v[N], w[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
for (int i = 1; i <= n; i ++)
for(int j = 0; j <= m; j ++)
{
f[i][j] = f[i - 1][j];
if (v[i] <= j) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0;
}
一维数组题解
-
为什么可以优化到一维数组版本呢?
-
f[i][j]
可以求得任意合法的i
与j
最优解,但是题目只要求得最终状态f[n][m]
,因此我们可以用一维数组更新 -
f[j]
: N件物品,背包容量为j
的最优解
-
-
为什么枚举背包容积
j
的时候从m
开始逆序呢?-
变量
i
的第n层循环时,f[N][j - v[i]]
比f[N][j]
先算出来, 所以f[N][j-v[i]]
是在第n层算出来的。 -
如果是逆序,
f[n][j]
要比f[n][j - v[i]]
先算出来,所以就是用f[n - 1][j - v[i]]
来更新f[j] = max(f[j], f[j - v[i]] + w[i]); // 如果是顺序怎等价为 f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])
-
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int n, m;
int f[N];
int v[N], w[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
for (int i = 1; i <= n; i ++)
for (int j = m; j >= v[i]; j --)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}