动态规划学习之LeetCode数组区间相关的题目(303、413)

题目:给定一个整数数组  nums,求出数组从索引 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

示例:

给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()

  sumRange(0, 2) -> 1
  sumRange(2, 5) -> -1
  sumRange(0, 5) -> -3

思路:

  1.定义一个状态转移变量,用于记录以当前正在遍历的元素的前一个元素结尾的序列的元素综合

  2.关系表达式:cur = num[i] + pre

  3.初始条件就是pre = 0

实现代码:

    int[] nums;
    public Tip303(int[] nums) {
        this.nums = nums;
    }

    public int sumRange(int i, int j) {
        if (i < 0 || j >= nums.length)
            return Integer.MIN_VALUE;
        int pre = 0;

        for (int k = i; k <= j; k++) {
            int cur = nums[k] + pre;
            pre = cur;
        }
        return pre;
    }

题目:数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N 。如果满足以下条件,则称子数组(P, Q)为等差数组:

   元素 A[P], A[p + 1], ..., A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。

   函数要返回数组 A 中所有为等差数组的子数组个数。

示例:

A = [1, 2, 3, 4]

返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。

分析: 因为对于数组(P,Q)含有一个P + 1 < Q的条件,所以可以得出一个等差数组至少含有三个元素,所以我们的解决办法就应该从最基本的三个元素的等差数组开始为着手点进行展开:

  1、首先定义一个状态转移数组dp,dp[i]表示以当前元素A[i]结尾的等差递增子区间的个数

  2、寻找数组dp的元素之间的关系:

    1)首先来分析,if  A[i] - A[]i-1] = A[i-1] - A[i-2],那么说明当前的元素A[i]能够和A[i-1]、A[i-2]构成一个含有三个元素的最基本的等差数组;

    2)然后,对于元素A[i-1]来说,以它结尾的等差递增子区间含有dp[i-1]个,那么如果A[i] - A[]i-1] = A[i-1] - A[i-2]这个条件成立,则能说明以A[i]结尾的等差递增子区间也会含有dp[i-1]个等差递增子区间,为什么呢?第一,dp[i]表示以当前元素A[i]结尾的等差递增子区间的个数,如果A[i] - A[]i-1] = A[i-1] - A[i-2]成立,那么可以看成是在以A[i-1]结尾的等差递增子区间的末尾再加上一个符合条件的元素,仍然是等差递增子区间,只不过是长度增加了1,所以dp[i-1]有多少个等差递增子区间,那dp[i]就会有多少个。

    3)所以最终的关系式是:dp[i] = dp[i-1] + 1;

  3、初始条件是,dp[0] = 0,dp[1] = 0

实现代码:

    public int numberOfArithmeticSlices(int[] A) {
        if (A.length <= 2)
            return 0;
        int[] dp = new int[A.length];
        dp[0] = 0;
        dp[1] = 0;
        int res = 0;
        for (int i = 2; i < A.length; i++) {
            if (A[i] - A[i-1] == A[i-1] - A[i-2]){
                dp[i] = dp[i-1] + 1;
                res += dp[i];
            }
        }
        return res;
    }

对上面的代码进行一下优化,尽量减少额外空间的使用,即不再使用状态转移数组,转而使用状态转移变量,即pre,表示当前正在遍历的元素的前一个元素,以它为结尾的等差递归子区间的个数。

    public int numberOfArithmeticSlices2(int[] A) {
        if (A.length <= 2)
            return 0;
        int res = 0;
        int pre = 0;
        // pre表示当前正在遍历的元素的前一个元素,以它为结尾的等差递归子区间的个数
        for (int i = 2; i < A.length; i++) {
            if (A[i] - A[i-1] == A[i-1] - A[i-2]){
                int cur = pre + 1;
                res += cur;
                pre = cur;
            }else {
                pre = 0;
            }
        }
        return res;
    }
原文地址:https://www.cnblogs.com/yxym2016/p/12640820.html