0. 前言
相关:
1. 完全背包裸题
背包问题枚举体积:
- 体积最多是
j
:大多数背包问题- 初始化: 假设为
f[0,i]
,表示一个物品都不选,且体积最大是i
的最大价值。那么就全部初始化为 0,因为所有物品都不选,那么体积不论是多少,其最大价值都是 0。这就只能求价值的最大值
- 初始化: 假设为
- 体积恰好是
j
:典型题:[完全背包] 买书(完全背包+裸题)
- 初始化:就只初始化
f[0][0]=0
,其余都初始化为INF
,这个是用来求价值的最小值。反之求最大值,其余都初始化为-INF
即可
- 初始化:就只初始化
- 体积至少是
j
:典型题:[01背包] 潜水员(01背包+二维费用背包)- 初始化:
f[0][0]=0
,其余是INF
,只能求价值最小值
- 初始化:
显然,该问题应该是属于第二种,且求解的是方案数。
完全背包问题: f[i][j]=max(f[i-1][j], f[i][j-v]+w)
,这个是经典的完全背包优化。优化到一维以后体积是从小到大顺序枚举的,这是与 01 背包很不同的一个地方。
思路:
- 状态定义:
f[i][j]
:从前i
个物品中选,总体积恰好等于j
的方案数
- 状态转移:
f[i][j] = f[i-1][j] + f[i][j-vi]]
,优化到一维就是f[j] += f[j-vi]
再就注意下本题,必须将钱 n
花完,才是正确的状态方案。可查看示例 2:输入 15 输出 0。显然,10 20 50 100 都是 10 的倍数,当输入总钱数能被 10 整除时,钱肯定就能花完。且状态从小到大进行转移,最后剩下的这个余数,比如 15,这个 15 就没办法转移过去,所有状态一直是 0。故,只有输入是 10 的倍数的时候,才能获取非 0 的方案数。
一维代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
int n;
int m[4] = {10, 20, 50, 100};
int f[N];
int main() {
cin >> n;
f[0] = 1;
for (int i = 0; i < 4; ++i) {
for (int j = m[i]; j <= n; j ++)
f[j] += f[j - m[i]];
}
cout << f[n] << endl;
return 0;
}
二维代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
int n;
int m[5] = {0, 10, 20, 50, 100}; // 方便 i-1
int f[5][N];
int main() {
cin >> n;
f[0][0] = 1;
for (int i = 1; i <= 4; ++i) {
for (int j = 0; j <= n; j ++) {
f[i][j] = f[i - 1][j];
if (j >= m[i]) f[i][j] += f[i][j - m[i]];
}
}
cout << f[4][n] << endl;
return 0;
}