一、 题目
1. 题目描述
给你三个整数 n
、m
和 k
。下图描述的算法用于找出正整数数组中最大的元素。
请你生成一个具有下述属性的数组 arr
:
arr
中有n
个整数。1 <= arr[i] <= m
其中(0 <= i < n)
。- 将上面提到的算法应用于
arr
,search_cost
的值等于k
。
返回上述条件下生成数组 arr
的 方法数 ,由于答案可能会很大,所以 必须 对 10^9 + 7
取余。
示例 1:
输入:n = 2, m = 3, k = 1 输出:6 解释:可能的数组分别为 [1, 1], [2, 1], [2, 2], [3, 1], [3, 2] [3, 3]示例 2:
输入:n = 5, m = 2, k = 3
输出:0
解释:没有数组可以满足上述条件
示例 3:
输入:n = 9, m = 1, k = 1 输出:1 解释:可能的数组只有 [1, 1, 1, 1, 1, 1, 1, 1, 1]示例 4:
输入:n = 50, m = 100, k = 25 输出:34549172 解释:不要忘了对 1000000007 取余示例 5:
输入:n = 37, m = 17, k = 7 输出:418930126
提示:
1 <= n <= 50
1 <= m <= 100
0 <= k <= n
Related Topics
- 动态规划
- 👍 69
- 👎 0
2. 原题链接
链接: 1420. 生成数组
二、 解题报告
1. 思路分析
- 撑了一下午不看题解,自己写了个*O(n2m2k)*版的。TLE了
- 看了下题解,终于做了下:
- 令f(n,m,k)为长度n的数组,最大数为m(注意必须用到m),上升次数为k的数组个数。
- 我们考虑数组中最后一个数,即f(i,mi,ki)会从f(i-1,…,…)中转移过来。
- 如果最后一个数的加入,没对k产生影响,则前n-1个数已经满足了上升k次,最后一个数就可以从[1,m]随便选。
则有 m*f(n-1,m,k)种组合。 - 如果最后一个数的加入,使k产生了变化,那一定让k+1,则这个数一定是m,前边的数没有m。
则有 sum{f(n-1,j,k-1)|j∈[1,m]}种组合 - 如果递归做需要考虑递归出口
- 官方题解用dp,甚至可以前缀和优化。
2. 复杂度分析
TLE版dfs: O(n2m2k)
普通dp或dfs: O(nm2k)
前缀和优化dp:O(nmk)
3. 代码实现
普通dfs
。
class Solution:
def numOfArrays(self, n: int, m: int, k: int) -> int:
mod = 10**9+7
"""题意:生成n个1到m之间([1,m])的数,且前缀最大值只上升k次(且最大的那个数最先出现的位置,从头计数max,必须上升k次)。
问有几种生成方法
"""
if k == 0:
return 0
@cache
def dfs(n,m,k):
if m == 1:
if k == 1:
return 1
return 0
if m < k:
return 0
if n == 1:
if k == 1:
return 1
return 0
ans = dfs(n-1,m,k)*m
# print(n,m,k,ans)
for j in range(1,m):
ans = (ans + (dfs(n-1,j,k-1))%mod)%mod
return ans
ret = 0
for i in range(1,m+1):
ret = (ret+dfs(n,i,k))%mod
return ret
"""
123 √
213 ×
"""
"""
11
22
21
"""
"""看了题解n*m*k*m,自己写:
1.令生成n个数上升k次的答案是:dfs(n,m,k),其中m是用了的数里的最大值
2.假设第n个数不改变k,那么这第n个数一定小于等于m,前n-1个数已经是一个完全满足k且最大值是m的子数组,共有dfs(n-1,m,k)*m
3.假设第n个数改变了k,那么这第n个数一定是m,前n-1个数里都小于m,那么前n-1个数的状态共有sum{dfs(n-1,j,k-1)|j∈[1,m-1]}
4.考虑出口:
"""
TLE版,请勿参考:
class Solution:
def numOfArrays(self, n: int, m: int, k: int) -> int:
mod = 10 ** 9 + 7
"""题意:生成n个1到m之间([1,m])的数,且前缀最大值只上升k次(且最大的那个数最先出现的位置,从头计数max,必须上升k次)。
问有几种生成方法
"""
if k == 0:
return 0
@cache
def q_pow1(a, b): # 快速幂
if b == 0:
return 1
if b == 1:
return a
if b & 1 == 0:
return q_pow(a * a % mod, b >> 1)
else:
return q_pow(a * a % mod, b >> 1) * a % mod
def q_pow(base, power):
res = 1
while power > 0:
if power & 1 == 1:
res = res * base % mod
power >>= 1
base = base * base % mod
return res % mod
@cache
def dfs(n, m, k):
if n == 0:
if k == 0:
return 1
return 0
if m == 1:
if k == 1:
return 1
return 0
if k == 0:
if n == 0:
return 1
return 0
ans = 0
# 遍历最大数x,它下标是j
for j in range(k - 1, n): # j前边至少要有k-1个数
for x in range(k, m + 1): # 比x小的数至少要有k-1个
r = q_pow(x, n - j - 1)
l = dp[j][x-1][k-1] = dfs(j, x - 1, k - 1)
ans = (ans % mod + (l % mod * r ) % mod) % mod
return ans
return dfs(n, m, k)
三、 本题小结
- 反正dp的题我看透了,我只能做出来我做的出来的题,做不出来的题下次遇到还是做不出来。