题目描述
这里有 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
官方题解(动态规划)
来源:https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/solutions/2490436/zhi-tou-zi-deng-yu-mu-biao-he-de-fang-fa-eewv/
我们可以使用动态规划解决本题。
记 f(i, j) 表示使用 i 个骰子且数字之和为 j 的方案数。为了计算该值,我们可以枚举最后一个骰子的数字,它的范围为 [1,k],那么状态转移方程即为:
f
(
i
,
j
)
=
∑
x
=
1
k
f
(
i
−
1
,
j
−
x
)
f(i, j) = \sum_{x=1}^{k} f(i-1, j-x)
f(i,j)=x=1∑kf(i−1,j−x)
动态规划的边界条件为:
f
(
0
,
0
)
=
1
f(0, 0) = 1
f(0,0)=1
即我们还没有掷骰子时,数字之和为0,以及:
f
(
i
,
j
)
=
0
i
f
j
<
0
f(i, j) = 0 \quad if\quad j < 0
f(i,j)=0ifj<0
即骰子的和不能小于0,在进行状态转移时,我们可以忽略所有 j - x < 0 的情况。
最终的答案为 f (n, target) 。
解法一
class Solution(object):
def numRollsToTarget(self, n, k, target):
"""
:type n: int
:type k: int
:type target: int
:rtype: int
"""
mod = 1e9 + 7
f = [[0] * (target + 1) for _ in range(n + 1)]
f[0][0] = 1
for i in range(1, n + 1):
for j in range(target + 1):
for x in range(1, k + 1):
if j - x >= 0:
f[i][j] = (f[i][j] + f[i - 1][j - x]) % mod
return int(f[n][target])
注意到状态转移放曾中,f(i,j)只会从 f(i−1,⋯ ) 转移而来,因此我们只需要存储当前行(第 i 行)和上一行(第 i−1 行)的值,即可以用两个一维数组代替上面代码中的二维数组 f 进行状态转移。
解法二
class Solution(object):
def numRollsToTarget(self, n, k, target):
"""
:type n: int
:type k: int
:type target: int
:rtype: int
"""
mod = 1e9 + 7
f = [1] + [0] * target
for i in range(1, n + 1):
g = [0] * (target + 1)
for j in range(target + 1):
for x in range(1, k + 1):
if j - x >= 0:
g[j] = (g[j] + f[j - x]) % mod
f = g
return int(f[target])
进一步,我们发现 f(i,j) 只会从第二维严格小于它的 f(i−1,⋯ ) 转移而来,因此我们可以倒序遍历第二维来计算 f(i,j),这样可以只使用一个一维数组。也就是说,当我们计算 f(i,j) 时,一维数组中下标大于 j 的位置都是 f(i,⋯ ) 的值,下标小于 j 的位置都是 f(i−1,⋯ ) 的值,这样就保证了 f(i,j) 结果的正确性。
解法三
class Solution(object):
def numRollsToTarget(self, n, k, target):
"""
:type n: int
:type k: int
:type target: int
:rtype: int
"""
mod = 1e9 + 7
f = [1] + [0] * target
for i in range(1, n + 1):
for j in range(target, -1, -1):
f[j] = 0
for x in range(1, k + 1):
if j - x >= 0:
f[j] = (f[j] + f[j - x]) % mod
return int(f[target])
**【碎碎念】动态规划出现了好多好多次,真的好好用啊!!