【Leetcode 每日一题】3250. 单调数组对的数目 I

问题背景

给你一个长度为 n n n 的 整数数组 n u m s nums nums。

如果两个 非负 整数数组 ( a r r 1 , a r r 2 ) (arr_1, arr_2) (arr1,arr2) 满足以下条件,我们称它们是 单调 数组对:

两个数组的长度都是 n n n。

  • a r r 1 arr_1 arr1 是单调 非递减 的,换句话说 a r r 1 0 ≤ a r r 1 1 ≤ . . . ≤ a r r 1 n − 1 arr_10 \le arr_11 \le ... \le arr_1n - 1 arr10≤arr11≤...≤arr1n−1
  • a r r 2 arr_2 arr2 是单调 非递增 的,换句话说 a r r 2 0 ≤ a r r 2 1 ≤ . . . ≤ a r r 2 n − 1 arr_20 \le arr_21 \le ... \le arr_2n - 1 arr20≤arr21≤...≤arr2n−1
  • 对于所有的 0 ≤ i ≤ n − 1 0 \le i \le n - 1 0≤i≤n−1 都有 a r r 1 i + a r r 2 i = = n u m s i arr_1i + arr_2i == numsi arr1i+arr2i==numsi

请你返回所有 单调 数组对的数目。

由于答案可能很大,请你将它对 1 0 9 + 7 10 ^ 9 + 7 109+7 取余 后返回。

数据约束

  • 1 ≤ n = n u m s . l e n g t h ≤ 2000 1 \le n = nums.length \le 2000 1≤n=nums.length≤2000
  • 1 ≤ n u m s i ≤ 50 1 \le numsi \le 50 1≤numsi≤50

解题过程

周赛三四题的水准,两道题目只在数据规模上有差异,目前只能尝试写灵神题解的解释。

这题虽然对两个数组有要求,但是实际上只需要枚举其中一个数组的情况,把对另外一个数组中元素的要求当成约束就行。

根据动态规划缩小问题规模的思想:

  • 原问题是下标 0 0 0 到 n − 1 n - 1 n−1中的单调数组对的个数,且 a r r 1 n − 1 = j = 0 , 1 , 2 , . . . , n u m s n − 1 arr_1n−1 = j = 0, 1, 2, ..., numsn - 1 arr1n−1=j=0,1,2,...,numsn−1
  • 子问题是下标 0 0 0 到 i i i 中的单调数组对的个数,且 a r r 1 i = j arr_1i = j arr1i=j,将其记作 d p i j dpij dpij

用 k k k表示 a r r 1 i − 1 arr_1i−1 arr1i−1,那么根据约束条件: { a r r 1 i − 1 ≤ a r r 1 i a r r 2 i − 1 ≥ a r r 2 i \ \begin{cases} arr_1i - 1 \le arr_1i \\ arr_2i - 1 \ge arr_2i \\ \end{cases} {arr1i−1≤arr1iarr2i−1≥arr2i,即 { k ≤ j n u m s i − 1 − k ≥ n u m s i − j \ \begin{cases} k \le j \\ numsi - 1 - k \ge numsi - j \\ \end{cases} {k≤jnumsi−1−k≥numsi−j,可以得到 k k k 的上界。

解得 k ≤ m i n ( j , n u m s i − 1 − n u m s i + j ) = j + m i n ( n u m s i − 1 − n u m s i , 0 ) k \le min(j, numsi - 1 - numsi + j) = j + min(numsi - 1 - numsi, 0) k≤min(j,numsi−1−numsi+j)=j+min(numsi−1−numsi,0),由于所有数组中的元素都是非负的,而 n u m s i = a r r 1 i + a r r 2 i numsi = arr_1i + arr_2i numsi=arr1i+arr2i,所以 k ≤ n u m s i − 1 k \le numsi - 1 k≤numsi−1

记 m a x K = j + m i n ( n u m s i − 1 − n u m s i , 0 ) maxK = j + min(numsi - 1 - numsi, 0) maxK=j+min(numsi−1−numsi,0),那么 d p i j = { 0 , m a x K < 0 Σ k = 0 m a x K d p i − 1 k , m a x K ≥ 0 dpij = \begin{cases} 0,& maxK \lt 0 \\ \mathop{\Sigma} \limits _ {k = 0} ^ {maxK} dpi - 1k,& maxK \ge 0 \\ \end{cases} dpij=⎩ ⎨ ⎧0,k=0ΣmaxKdpi−1k,maxK<0maxK≥0。

这里 Σ k = 0 m a x K d p i − 1 k \mathop{\Sigma} \limits _ {k = 0} ^ {maxK} dpi - 1k k=0ΣmaxKdpi−1k 表示对所有的 k k k 从 0 0 0 到 m a x K maxK maxK 的情况,求下标 0 0 0 到 i − 1 i - 1 i−1 中的单调数组对的个数之和,要求 a r r 1 = k arr_1 = k arr1=k。这显然满足前缀和的定义,记 s j = Σ k = 0 j d p i − 1 k sj = \mathop{\Sigma} \limits _ {k = 0} ^ {j} dpi - 1k sj=k=0Σjdpi−1k,那么上述状态转移方程 ( 3 ) (3) (3) 可以简化为 d p i j = { 0 , m a x K < 0 s m a x K , m a x K ≥ 0 dpij = \begin{cases} 0,& maxK \lt 0 \\ smaxK,& maxK \ge 0 \\ \end{cases} dpij={0,smaxK,maxK<0maxK≥0。

初始值 d p 0 j = 1 dp0j = 1 dp0j=1,其中 j = 0 , 1 , 2 , . . . , n u m s 0 j = 0, 1, 2, ..., nums0 j=0,1,2,...,nums0。这表示的是,下标 0 0 0 到 0 0 0 中的单调数组对的个数,也就是只考虑数组中第一个元素的情况, a r r 1 i arr_1i arr1i 可以是合法范围内任意值。

后续还有进一步优化,目前一下子掌握不了,先暂时放弃。

具体实现

java 复制代码
class Solution {

    // 根据题意定义模数
    private static final int MOD = 1000000007;

    public int countOfPairs(int[] nums) {
        int n = nums.length;
        // m 表示 nums 数组中的最大值,所有数组中的元素都不会超过这个范围,也就是应枚举的最大值
        int m = Arrays.stream(nums).max().getAsInt();
        long [][] dp = new long[n][m + 1];
        long[] preSum = new long[m + 1];
        // 填充初始值,只考虑数组中第一个元素的情况
        Arrays.fill(dp[0], 0, nums[0] + 1, 1);
        for(int i = 1; i < n; i++) {
            // 计算前缀和,这是它的定义
            preSum[0] = dp[i - 1][0];
            for(int k = 1; k <= m; k++) {
                preSum[k] = (preSum[k - 1] + dp[i - 1][k]) % MOD;
            }
            for(int j = 0; j <= nums[i]; j++) {
                int maxK = j + Math.min(nums[i - 1] - nums[i], 0);
                dp[i][j] = maxK >= 0 ? preSum[maxK] % MOD : 0;
            }
        }
        // 答案是考虑完数组中最后一个元素之后,对所有可能情形求和
        return (int) (Arrays.stream(dp[n - 1], 0, nums[n - 1] + 1).sum() % MOD);
    }
}
相关推荐
め.1 分钟前
定点数运算库
算法·unity
yuannl106 分钟前
图的深度优先遍历DFS
数据结构
dyxal9 分钟前
最长公共子序列(LCS)
python·算法
稚南城才子,乌衣巷风流2 小时前
ST 表(Sparse Table)算法详解:原理、实现与应用
算法
hold?fish:palm2 小时前
9 找到字符串中所有字母异位词
c++·算法·leetcode
Sw1zzle3 小时前
算法入门(六):贪心算法 - 基础入门(Leetcode 121/455/860/376/738)
算法·leetcode·贪心算法
青山木3 小时前
Hot 100 --- 岛屿数量
java·数据结构·算法·leetcode·深度优先·广度优先
不会就选b3 小时前
算法日常・每日刷题--<归并排序>1
数据结构·算法
危桥带雨4 小时前
排序算法(快排、归并、计数、基数排序)
数据结构·算法·排序算法