【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);
    }
}
相关推荐
Dream it possible!11 小时前
LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)
算法·leetcode·面试·动态规划
心抵鹊12 小时前
算法之双指针系列(一)
算法·leetcode
十二12 小时前
【洛谷题解】P1083 [NOIP 2012 提高组] 借教室(二分答案 + 差分)
数据结构·c++·算法·蓝桥杯
小此方13 小时前
[算法实战论:动态规划]回文串问题的dp解法,回文串问题的母题,以及以此为基础的派生题
算法·动态规划
Pniubi13 小时前
力扣560.和为k的子数组
算法·leetcode·职场和发展
D e re k ZH14 小时前
C++ 从零入门(一):基础语法与程序结构
数据结构·c++·算法
Pniubi14 小时前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
東隅已逝,桑榆非晚17 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom17 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战
bro_Java66618 小时前
《排序的底层逻辑:比较、交换与分治》
java·算法·排序算法