题意
- n个甜点,每个甜点有一个价值,每个甜点可以选和不选;
- m个魔法棒,每个魔法棒可以对选择的甜点使用至多一次,使它的价值变为原来的阶乘;
- 期望总和S,即选择的甜点期望达到价值总和值S
- 问题为在以上给定条件下,达到S的方案数
思路
简单分析及预处理
- 首先根据题意估计一下时间复杂度, 须小于
1e9,而阶乘在时就已经达到了1e6,故设定上限不超过10的阶乘3628800 - 故此我们可以预处理1-10的阶乘以避免重复计算数组中的阶乘,对于10以上的返回3628800即可因为大概率用不上
- 分析该题是计算方案数,由于有明显的递推性(选和不选,用和不用),首先考虑动态规划再进行优化
状态转移分析
- 状态定义及初始化
f[i][j][k]:表示前i个甜点中,当前拥有j个魔法棒,使得喜爱值之和为k的方案数。f[i][j][0] = 1,表示不选择任何甜点,不使用任何魔法棒,喜爱值之和为 0 的方案数为 1。
- 状态转移
- 对于每个甜点
i,我们可以选择累计到总和,或者不选择,选择后亦可选择使用魔法棒或者不使用。 - 状态转移方程可以表示为:
f[i][j][k] = f[i-1][j][k] + f[i-1][j][k-like[i-1]] + f[i-1][j-1][k-getf(like[i-1])]
其中,f[i-1][j][k] 表示不选择第 i 个甜点,f[i-1][j][k-like[i-1]] 表示选择第 i 个甜点但不使用魔法棒,f[i-1][j-1][k-getf(like[i-1])] 表示选择第 i 个甜点并使用魔法棒。
- 其中op数组表示预处理后对应的阶乘数组
- 边界分析
- 当
k < like[i-1]或k < getf(like[i-1])时,对应的转移项不合法,需要跳过。
优化以及代码
- 我们可以使用滚动数组来优化空间
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int f[2][N][N]; // 动态规划数组,f[i][j][k]表示前i个甜点中至多使用j个魔法棒,喜爱值之和为k的方案数
int p[N]; // 存储阶乘值的数组
// 预处理阶乘值
inline void getpre() {
p[1] = 1; // 1的阶乘为1
for (int i = 2; i <= 10; i++)
p[i] = i * p[i - 1]; // 计算2到10的阶乘
}
// 获取甜点喜爱值的阶乘值
int getf(int x) {
if (x <= 10)
return p[x]; // 如果喜爱值小于等于10,直接返回对应的阶乘值
return 362880; // 否则返回10的阶乘值(362880)
}
// 主函数,计算满足条件的方案数
int solution(int n, int m, int s, std::vector<int> like) {
getpre(); // 预处理阶乘值
vector<int> op(n); // 存储每个甜点的阶乘喜爱值
for (int i = 0; i < n; i++) {
op[i] = getf(like[i]); // 计算每个甜点的阶乘喜爱值
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= s; k++) {
f[i&1][j][k] = 0; // 初始化动态规划数组
if (!k) { // 如果喜爱值之和为0
f[i&1][j][k] = 1; // 方案数为1(不选任何甜点)
continue;
}
if (i) { // 如果当前甜点数大于0
f[i&1][j][k] += f[i - 1&1][j][k]; // 不选当前甜点
if (k >= like[i - 1]) { // 如果当前喜爱值之和大于等于当前甜点的喜爱值
f[i&1][j][k] += f[i - 1&1][j][k - like[i - 1]]; // 选当前甜点但不使用魔法棒
if (j && k >= op[i - 1]) // 如果还有魔法棒且当前喜爱值之和大于等于当前甜点的阶乘喜爱值
f[i&1][j][k] += f[i - 1&1][j - 1][k - op[i - 1]]; // 选当前甜点并使用魔法棒
}
}
}
}
}
return f[n&1][m][s]; // 返回前n个甜点中至多使用m个魔法棒,喜爱值之和为s的方案数
}
int main() {
// 你可以添加更多测试用例
std::vector<int> like1 = {1, 2, 3};
std::vector<int> like2 = {1, 1, 1};
std::vector<int> like3 = {1, 2, 3, 4, 5};
std::cout << (solution(3, 2, 6, like1) == 5) << std::endl; // 测试样例1
std::cout << (solution(3, 1, 1, like2) == 6) << std::endl; // 测试样例2
std::cout << (solution(5, 3, 24, like3) == 1) << std::endl; // 测试样例3
return 0;
}