vlambda博客
学习文章列表

力扣hard——1420.生成数组(动态规划)

力扣第185场周赛最后一题生成数组,挺有意思的一道题

先来看看题目

题目解析:

首先,此题给出的代码就是一个在数组中寻找最大值的代码

其中的search_cost代表的就是在寻找过程中更新了几次最大值,也就是有多少对相邻的两个数是升序排列的。

再看题目要求,结合刚才的分析,可以把题目抽象成:

要求有多少个有n个整数、整数大小大于1小于m、有k对相邻的两个数是升序排列的数组的数量。


解题思路:

我们可以很快的想到一个暴力解法,枚举所有有n个整数、数大小大于1小于m的数组,然后利用题目给出的代码检测这些数组的search_cost,为k则答案加1。


此暴力解法复杂度为O(n*(m^n)),这是一个指数级的复杂度,根据题目给出的n,m的范围,那么复杂度可以达到O(50*(100^50)),这个复杂度就是神威太湖之光也算不来。


接下来我们想想有什么办法可以优化,首先分治的思路对于这题肯定是行不通的,因为问题无法无法分割为两个独立的子问题。贪心算法也行不通,因为这个问题是要求一个数量,不是要求一个问题的最优解。


那么还有什么算法适合这个问题呢?


动态规划可行吗?

要判断动态规划是否可行,就要先来看看这个问题是否可以分解为几个重叠的子问题。


长度为n-1数组中最大值为msearch_cost为p的数组A0,这样的数组数量为x0

长度为n-1数组中最大值为i(1=<i<m)search_cost为p-1的数组Ai,这样的数组数量为xi

长度为n数组中最大值为msearch_cost为p的数组B,这样的数组数量为y


我们仔细想想,数组A0后面加上任意一个值在[1,m]之间的数是不是就变成了数组B,而数组Ai后面加上任意一个值在[i+1,m]之间的数是不是也变成了数组B。这就对了,数组B就是由数组A0-Am在后面添加一个数演变过来的。所以这道题可以分解为几个重叠子问题,可以用动态规划求解。


那么我们设dp[n][m][p]表示长度为n数组中最大值为msearch_cost为p的数组数量,可以得到如下的状态转移方程


力扣hard——1420.生成数组(动态规划)

此算法复杂度O(n*m*k),按照题目给出的范围也就是O(250000),这个复杂度完全可以通过,OK,接下来就是码代码,注意由于结果太大,所以结果要对1000000007取模。


代码

class Solution { public static final int mod = 1000000007; public int numOfArrays(int n, int m, int k) { if (m<k||n<k)//不存在这样的数组 return 0; int[][][] dp = new int[n + 1][m + 1][k + 1]; for (int i = 1;i<=n;i++)//初始化,全为1的数组只有一个 dp[i][1][1] = 1; for (int j = 1;j<=m;j++)//初始化,长度为1,最大值为j,cost为1的数组只有一个 dp[1][j][1] = 1; for (int i = 2;i<=n;i++) for (int p = 1;p<=k;p++) for(int j = 2,sum = dp[i-1][1][p-1];j<=m;j++,sum=(sum+dp[i-1][j-1][p-1])%mod) dp[i][j][p] = (sum+(int)(((long)dp[i-1][j][p]*j)%mod))%mod; int ans = 0; for (int j = 1;j<=m;j++)//最后结果为长为n,最大值为j,cost为k的数组数量的和 ans = (ans+dp[n][j][k])%mod; return ans; }}