目录
题目
Description
龙神有很多背包,每一个背包都有一个容积。但是这些背包的容积都恰好是一个数字的整数倍,比如,,等等。并且对于任意,容积为的背包都存在。
龙神有一些物品要装进背包,第个物品占据的体积。现在,龙神想选出一些物品,使得存在一个背包可以恰好放下这些物品,并且这个背包放满。
龙神想知道这样的取法有多少个,请你帮他算一算吧?由于取法很多,你只需要输出取法的末七位数,没有前导零,即可(即对10000000取模)。
Input
第一行两个数,分别表示物品数和背包体积的基数。
第二行个数,分别表示每个物品的体积。
Output
输出一行一个数,表示取法总数的末七位。
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0
|
思路
1.确定dp数组及其下标的含义
dp数组用于记录背包容量为j时的取法总数,其下标表示背包容量。
数组dp的长度为2*V+1,其中V是背包体积的基数。
2.确定递推公式
对于每个物品i,背包容量j,存在递推公式dp[j] = dp[j] + dp[j - p[i]]。其中,dp[j]代表当前背包容量j下的取法总数,dp[j-p[i]]表示加上当前物品体积后的背包容量(即j-p[i])下的取法总数。
3.dp数组初始化
首先将dp数组所有元素都初始化为0。然后初始化dp[0] = 1,表示背包容量为0时有一种取法。
4.遍历顺序
使用两个嵌套的for循环进行遍历。
外层循环遍历每个物品,内层循环从背包容量的最大值2V递减到物品体积p[i]。
这种遍历顺序确保在计算dp[j]时,可以利用上一轮计算的dp[j-p[i]]的值。另外,为了保证在更新dp[j]时,不影响后续计算,第二个内层循环是从2V递减到p[i]的,这样可以避免重复更新dp[j-p[i]]
注意事项
-
题目要求输出取法总数的末七位数,即对结果取模10000000。在代码中,对dp数组元素进行累加操作时,要注意取模运算,以防止数值溢出。
-
题目给出了背包容量的基数V,即背包容量都是V的整数倍。在读取每个物品的体积后,需要对其进行取模运算,以保证它们都能装进背包,如果取模结果为0,则将其置为V。
-
使用long long存储数据
C++完整代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, V;
cin >> n >> V;
// 创建一个长度为n+1的数组p来存储每个物品的体积
vector<long long> p(n + 1);
for (int i = 1; i <= n; i++) {
cin >> p[i];
p[i] %= V;
if (p[i] == 0)
p[i] = V;
}
// 初始化dp数组,长度为2*V+1,用来记录背包容积为i时的取法总数
vector<long long> dp(2 * V + 1, 0);
dp[0] = 1;
// 动态规划求解
for (int i = 1; i <= n; i++) {
for (int j = 2 * V; j >= p[i]; j--) {
// 更新dp[j]为原来的值加上dp[j-p[i]]
dp[j] = (dp[j] + dp[j - p[i]]) % 10000000;
}
for (int j = 2 * V; j >= p[i]; j--) {
if (j > V) {
// 如果背包容积j大于V,则将dp[j-V]加上dp[j],并将dp[j]置为0
dp[j - V] = (dp[j - V] + dp[j]) % 10000000;
dp[j] = 0;
}
}
}
// 输出答案,即背包容积为V时的取法总数的末七位数
cout << dp[V] % 10000000 << endl;
return 0;
}