1. 题目来源
链接:5391. 生成数组
2. 题目说明
3. 题目解析
方法一:暴力+动态规划+巧妙解法
数据范围很小,完全可以暴力 dp
,也是多数人所采取的方法了。理解题意的话就是 search_cost
代表着从前向后遍历该数组时,同时记录一个最大值 max
,这个 search_cost
就是 max
所更新的次数。dp
思路如下:
- 状态定义:
dp[i][p][v]
前i
个数字,search_cost=p
,当前的最大值为v
的方案数 - 四重暴力循环顺序递推
dp
数组即可- 第一重
for
:从 1 到n
的递推,范围[1, n]
- 第二重
for
:前一个状态search_cost=p
,范围[0, k]
- 第三重
for
:前一个状态最大值为l
,范围[0, m]
- 至此,前三重表示前一个状态,即
dp[i - 1][p][l]
转移到当前状态 - 第四重
for
:当前状态所填的数字v
,范围[1 m]
- 第一重
- 在第四重
for
内进行状态更新,即会产生两种情况:v > l
时则最大值需更新,且search_cost
需加 1v ≤ l
时则不需要进行最大值和search_cost
的更新
这个 dp
,几乎将能算的状态全算出来了,相当暴力。个人感觉理解的重点在于区别前三重 for
的作用是确定上一状态的,第四重 for
表示现在的数字选取及内部状态转移,np
继承上状态的 search_cost
,nv
继承上状态的最大值,然后比较转移即可,确实很直观,但由于循环嵌套确实多,不熟练的也是望而生畏的。
参见代码如下:
// 执行用时 :272 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :9.4 MB, 在所有 C++ 提交中击败了100.00%的用户
#define LL long long
const LL MOD = 1e9 + 7;
// dp[i][k][v] 前i个数字,search_cost=k,当前的最大值为v的方案数
LL dp[55][55][150];
class Solution {
public:
int numOfArrays(int n, int m, int k) {
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int p = 0; p <= k; ++p) {
for (int l = 0; l <= m; ++l) {
for (int v = 1; v <= m; ++v) {
int np = p, nv = max(v, l);
if (v > l) ++np;
dp[i][np][nv] = (dp[i][np][nv] + dp[i - 1][p][l]) % MOD;
}
}
}
}
LL ans = 0;
for (int v = 1; v <= m; ++v) {
ans = (ans + dp[n][k][v]) % MOD;
}
return ans;
}
};