「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
背包问题(dp)
化零散为整体,使用f(i,j)表示一大类集合
化整为零,f(i,j)是由谁转化来的(如何得出)
背包问题思维方式
首先确定f [ i , j ] 函数的定义
确定定义后通过图片进行分析,列好f [ i , j ] 从实际意义出发考虑初始化
方案数问题
- 考虑到前i个然后总和至多,不超过,小于等于j的时候f(所有)初始化成1
- 考虑到前i个然后总和恰好等于j的时候f(O) = 1其余都是0
- 考虑到前i个然后总和至少是j的时候f(O) = 1其余都是0转移的时候可以要j <v从f (0)转移过来所以j可以从0开始枚举
最大最小值问题
不超过
- 考虑到前i个物品体积不超过,至多,小于等于j的所有方案的价值最大值f(所有) = 0
- 所以体积不超过j的时候我们不讨论最小值
恰好等于
- 考虑到前i个物品体积恰好等于j的所有方案的价值最大值f (0) = 0 f(其余) - 0x3f3f3f3f
- 最小值f (0) =0f (其余) 0x3f3f3f3f
至少
- 考虑到前i个物品体积至少是j的所有方案的价值最大值我们不讨论
- 最小值f (0) = 0 f(其余) 0x3f3f3f3f状态转移j < v可以从0转过来
01背包
n个物品(每个物品只有一个),每个物品v体积,w价值,有m个背包,如何取价值最大化
分析:
F[ i , j ] = max ( F [ i - 1 , j ] , F [ i - 1 , j - v] + w )
该公式表示:F[ i , j ] 从前 i 个物品中选出了总体积为 j 的物品放入背包的最大价值和
F [ i - 1 , j ] 表示不选第 i 个物品的最大价值
F [ i - 1 , j - v ] + w (条件:j >= v) 表示不选第 i 个物品的最大价值
模板
求最大价值
优化前
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int f[N][N];//用于表示F[i,j]
int n, m;
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
int v, w;
cin >> v >> w;
for (int j = 1; j <= m; j ++ ){
if(j - v >= 0) f[i][j] = max(f[i - 1][j], f[i - 1][j - v] + w);//如果有容量,就取最大值
else f[i][j] = f[i - 1][j];
}
}
cout << f[n][m] << endl;
return 0;
}
优化后
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int f[N];
int n, m;
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
int v, w;
cin >> v >> w;
for (int j = m; j >= v; j -- ){
f[j] = max(f[j], f[j - v] + w);//因为优化前只使用了i-1所以可以直接将二维数组转化为一维数组
}
}
cout << f[m] << endl;
return 0;
}
求方案数
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
int n, m;
int f[N];//f[i,j]的含义是,前i个物品值为j的方案数
int main()
{
cin >> n >> m;
f[0] = 1;
for (int i = 0; i < n; i ++ )
{
int v;
cin >> v;
for (int j = m; j >= v; j -- )
f[j] += f[j - v];//max函数变成了+发
}
cout << f[m] << endl;
return 0;
}