动态规划·dp·01背包
本帖部分内容取自OI wiki,若有侵权立即删除,特此声明
01 背包
1. 引入
考虑下面这种问题
已知有
这就是典型的01 背包问题
2.思考过程
01 背包是典型的背包问题,而大多背包问题可以用动态规划(DP)解决。
设一个存储DP状态的数组
接下来考虑状态转移方程。
假设当前已经处理好了前
当其放入背包时,背包的剩余容量会减小
由此,可以得出状态转移方程为
请记住该方程式,这个式子很重要。
3.代码实现
我们使用双层嵌套循环来枚举每个状态
for (int i = 1; i <= n; i++)
{
for (int j = w[i]; j <= W; j++)
{
}
}
事实上,这正是完全背包问题的解法
然后请出我们的状态转移方程,就得到了01 背包的核心代码。
//核心代码
for (int i = 1; i <= n; i++)
{
for (int j = w[i]; j <= W; j++)
{
f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
}
}
由此,我们就能解决开头所提出的问题了。
#include <bits/stdc++.h> //万能头文件
using namespace std;
int n, v[105], w[105], W, f[105][105];
int main()
{
cin >> n >> W; //读入数据
for (int i = 1; i <= n; i++)
{
cin >> w[i] >> v[i]; //读入大小与价值
}
for (int i = 1; i <= n; i++)
{
for (int j = w[i]; j <= W; j++)
{
f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]); //状态转移方程
}
}
cout << f[n][W];
return 0;
}
4.优化代码
刚才我们使用的是二维数组,容易MLE。能不能优化一下空间呢?能。
我们可以用一维数组,这样空间能减少一半。
运用滚动数组的思路,我们可以继续思考:
由于对
这其中,
然后,我们改一下我们的核心代码:
for (int i = 1; i <= n; i++)
{
for (int j = W; j >= w[i]; j--) //倒序枚举!!!很重要!!!
{
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
}
为什么内层循环要倒序枚举呢?
对于当前处理的物品
这样,我们就可以优化刚才的代码了:
#include <bits/stdc++.h>
using namespace std;
int n, v[105], w[105], W, f[105];
int main()
{
cin >> n >> W;
for (int i = 1; i <= n; i++)
{
cin >> w[i] >> v[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = W; j >= w[i]; j--)
{
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
}
cout << f[W];
return 0;
}