题目是LeetCode第185场周赛的第四题,链接:生成数组。具体描述见原题。
这道题是hard难度的,然后不出意料又是动态规划的题目,难点自然在于递推公式。定义dp[i][j][p]代表对于前i个数,最大的数为j,然后search cost为p时的符合条件的数组总数。那么对于前i个数且最大值为j、search cost为k的这种情况,又可以分为两种情况:
- 最大值
j在位置1~i-1之间,则第i个位置上的数可以是1~j,则此时的总数为j * dp[i-1][j][k],这里的dp[i-1][j][k]就是前i-1个数里刚好最大值为j且search cost为k时的总数; - 最大值
j在第i个位置,则此时的方法数就是前i-1个数最大值为1~j-1这j-1种情况、search cost为k-1的j-1个总数之和: ∑ p = 1 j − 1 d p [ i − 1 ] [ p ] [ k − 1 ] \sum_{p=1}^{j-1}dp[i-1][p][k-1] ∑p=1j−1dp[i−1][p][k−1]
总结一下,就是
d
p
[
i
]
[
j
]
[
k
]
=
j
×
d
p
[
i
−
1
]
[
j
]
[
k
]
+
∑
p
=
1
j
−
1
d
p
[
i
−
1
]
[
p
]
[
k
−
1
]
dp[i][j][k]=j\times dp[i-1][j][k]+\sum_{p=1}^{j-1}dp[i-1][p][k-1]
dp[i][j][k]=j×dp[i−1][j][k]+∑p=1j−1dp[i−1][p][k−1]。然后边界条件为dp[i][1][1]=1(对应于全部i个数全为1的情况)以及dp[1][j][1]=1(对应于只有一个数的情况,这个数只能是j)。最终的结果就是
∑
p
=
1
m
d
p
[
n
]
[
p
]
[
k
]
\sum_{p=1}^{m}dp[n][p][k]
∑p=1mdp[n][p][k]。
时间复杂度为 O ( n m 2 k ) O(nm^{2}k) O(nm2k),空间复杂度为 O ( m n k ) O(mnk) O(mnk)。
JAVA版代码如下:
class Solution {
public int numOfArrays(int n, int m, int k) {
if (k == 0 || m < k) {
return 0;
}
int mod = 1000_000_007;
// dp[i][j][p]:前i个数,最大的为j,search cost为p
int[][][] dp = new int[n + 1][m + 1][k + 1];
for (int i = 1; i <= n; ++i) {
dp[i][1][1] = 1;
}
for (int j = 1; j <= m; ++j) {
dp[1][j][1] = 1;
}
for (int i = 2; i <= n; ++i) {
for(int j = 2; j <= m; ++j) {
for (int p = 1; p <= k; ++p) {
int sum = 0;
for (int q = 1; q <= Math.min(j - 1, m); ++q) {
sum = (sum + dp[i - 1][q][p - 1]) % mod;
}
int temp = (int)((dp[i - 1][j][p] * 1L * j) % mod);
dp[i][j][p] = (sum + temp) % mod;
}
}
}
int result = 0;
for (int j = 1; j <= m; ++j) {
result = (result + dp[n][j][k]) % mod;
}
return result % mod;
}
}
提交结果如下:
上面的代码有个问题就是求
∑
p
=
1
j
−
1
d
p
[
i
−
1
]
[
p
]
[
k
−
1
]
\sum_{p=1}^{j-1}dp[i-1][p][k-1]
∑p=1j−1dp[i−1][p][k−1]费时得很,但其实只要我们稍微改一下三个循环的顺序,把对p的循环提到对j的循环之前就可以借助于对j的循环求得上面的求和。从而减少时间复杂度为
O
(
n
m
k
)
O(nmk)
O(nmk),空间复杂度还是
O
(
m
n
k
)
O(mnk)
O(mnk)。
JAVA版代码如下:
class Solution {
public int numOfArrays(int n, int m, int k) {
if (k == 0 || m < k) {
return 0;
}
int mod = 1000_000_007;
// dp[i][j][p]:前i个数,最大的为j,search cost为p
int[][][] dp = new int[n + 1][m + 1][k + 1];
for (int i = 1; i <= n; ++i) {
dp[i][1][1] = 1;
}
for (int j = 1; j <= m; ++j) {
dp[1][j][1] = 1;
}
for (int i = 2; i <= n; ++i) {
for (int p = 1; p <= k; ++p) {
int sum = 0;
for(int j = 2; j <= m; ++j) {
sum = (sum + dp[i - 1][j - 1][p - 1]) % mod;
int temp = (int)((dp[i - 1][j][p] * 1L * j) % mod);
dp[i][j][p] = (sum + temp) % mod;
}
}
}
int result = 0;
for (int j = 1; j <= m; ++j) {
result = (result + dp[n][j][k]) % mod;
}
return result % mod;
}
}
提交结果如下:
Python版代码如下:
class Solution:
def numOfArrays(self, n: int, m: int, k: int) -> int:
if k == 0 or k > m:
return 0
mod = 1000000007
dp = [[[0 for _ in range(k + 1)] for _ in range(m + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
dp[i][1][1] = 1
for j in range(1, m + 1):
dp[1][j][1] = 1
for i in range(2, n + 1):
for p in range(1, k + 1):
s = 0
for j in range(2, m + 1):
s = (s + dp[i - 1][j - 1][p - 1]) % mod
dp[i][j][p] = (s + j * dp[i - 1][j][p]) % mod
result = 0
for j in range(1, m + 1):
result += dp[n][j][k]
return result % mod
提交结果如下:
本文深入探讨了LeetCode第185场周赛第四题的动态规划解决方案,详细阐述了递推公式及其背后的逻辑,提供了优化的时间复杂度从O(nm^2k)到O(nmk)的策略,并附上了JAVA及Python的实现代码。


被折叠的 条评论
为什么被折叠?



