动态规划:446. 等差数列划分 II - 子序列(困难)

给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。

如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该序列为等差序列。

        例如,[1, 3, 5, 7, 9]、[7, 7, 7, 7] 和 [3, -1, -5, -9] 都是等差序列。

        再例如,[1, 1, 2, 5, 7] 不是等差序列。

数组中的子序列是从数组中删除一些元素(也可能不删除)得到的一个序列。

        例如,[2,5,10] 是 [1,2,1,2,4,1,5,10] 的一个子序列。

示例 1:  输入:nums = [2,4,6,8,10]

                输出:7

                解释:所有的等差子序列为:

                        [2,4,6]

                        [4,6,8]

                        [6,8,10]

                        [2,4,6,8]

                        [4,6,8,10]

                        [2,4,6,8,10]

                        [2,6,10]

解题思路:该题可以使用动态规划进行求解,d=nums[i]-num[j],   dp[i][d] = dp[i][d] + dp[j][d] + 1 ;指的是可以跟{num[i],num[j]}一起组等差子序列的个数,即最后一项为 nums[i],公差为 d 的等差子序列的个数。

                代码实现时,由于nums[i] 的范围很大,所以计算出的公差的范围也很大,我们可以将状态转移数组dp的第二维用哈希表 f  代替。

                 另外,如果nums[i]−nums[j]=d,那么在这一轮遍历中,j 会遍历到与上一轮相同的位置,答案增加的次数相同,并且额外多出了j的{各个等差子序列,nums[i+1]}  这些等差数列,因此有:

                                                                                等差子序列总数ans = ans + dp[j][d];


public int numberOfArithmeticSlices(int[] nums) {

        int ans = 0;

        int l = nums.length;

        Map[] f = new Map[l];  // 状态转移表

        for(int i=0; i

            f[i] = new HashMap(); //初始化 状态转移表

        }

        for(int i=0; i

            for(int j=0; j

                Long d = 1L * nums[i] - nums[j];  //得到公差

                int cnt = f[j].getOrDefault(d, 0);  //得到dp[j][d]的值

                ans += cnt;  //  等差子序列总数

                f[i].put(d, f[i].getOrDefault(d, 0) + cnt + 1); // dp[i][d] = dp[i][d] + dp[j][d] + 1

            }

        }

        return ans;

    }

你可能感兴趣的:(动态规划:446. 等差数列划分 II - 子序列(困难))