目录
1155. 掷骰子等于目标和的方法数
题目描述:
这里有 n
个一样的骰子,每个骰子上都有 k
个面,分别标号为 1
到 k
。
给定三个整数 n
, k
和 target
,返回可能的方式(从总共 kn
种方式中)滚动骰子的数量,使正面朝上的数字之和等于 target
。
答案可能很大,你需要对 109 + 7
取模 。
示例 1:
输入:n = 1, k = 6, target = 3 输出:1 解释:你扔一个有 6 个面的骰子。 得到 3 的和只有一种方法。
示例 2:
输入:n = 2, k = 6, target = 7 输出:6 解释:你扔两个骰子,每个骰子有 6 个面。 得到 7 的和有 6 种方法:1+6 2+5 3+4 4+3 5+2 6+1。
示例 3:
输入:n = 30, k = 30, target = 500 输出:222616187 解释:返回的结果必须是对 109 + 7 取模。
提示:
1 <= n, k <= 30
1 <= target <= 1000
实现代码与解析:
动态规划
class Solution {
public:
int mod = 1e9 + 7;
int numRollsToTarget(int n, int k, int target) {
vector<int> f(target + 1);
f[0] = 1; // 初始化,背包大小为0时,没有结果,为一种选择,赋值为1
// 分组背包,每组只能选一个
for (int i = 1; i <= n; i ++){ // 分组
for (int j = target; j >= 0; j--){ // 体积
f[j] = 0; // 每个筛子都要放入背包,不能跳过,把上一次的状态清空,这是一维与一般的背包问题不同的地方
for (int m = 1; m <= k ; m++){ // 价值
if (j >= m) { // 剩余体积大才能选,由于优化成了一维,所以j < m时,就直接继承了上一滚动数组的值,不用我们单独赋值了
f[j] = (f[j - m] + f[j]) % mod; // f[i][j] = f[i - 1][j - m] + f[i][j]; 求组合个数的递推式,当前方案,加上上一个方案
}
}
}
}
return f[target];
}
};
原理思路:
转化为分组背包问题,注释写的非常详细了,不再解析,只解释一些f[i] = 0 的代码。
这个清零的操作确保了在每个迭代中,f[j]
包含了只考虑当前骰子面值的组合数,而不会累积之前迭代的结果。这是解决问题的关键,以确保计算的正确性,因为你不能跳过任何一个骰子的面值,而传统的分组背包是可以跳过的。