等差数列划分--子序列问题DP解决

Posted C_YCBX Py_YYDS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了等差数列划分--子序列问题DP解决相关的知识,希望对你有一定的参考价值。

题目


视频讲解

暴力回溯构建等差数组+数学方法优化

当出现完全一样元素大小的长度很长的数组数组时,可计算Cn1…Cnn来实现。

超时,差最后一个case

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        using ll = long long;
        int n = nums.size();
        if(n<3)return 0;
        auto check = [&](){
            int b = nums[0];
            for(auto t:nums){
                if(b!=t)
                    return false;
            }
            return true;
        };
        if(check()){
            ll res = 1<<n;
            //减去Cn1-Cn2-Cn0
            res -= n;
            res -= n*(n-1)/2;
            res -= 1;
            return res;
        }
        int cnt = 0;
        //backtrack维护一个等差数组
        function<void(vector<ll>&,int)> backtrack = [&](vector<ll>&t,int pos){
            if(t.size()>2)
                cnt++;
            for(int i=pos;i<n;i++){
                if(t.size()<2){
                    t.emplace_back(nums[i]);
                    backtrack(t,i+1);
                    t.pop_back();
                }else{
                    int sz = t.size();
                    ll gap = t[sz-1] - t[sz-2];
                    if(gap==(ll)nums[i]-t[sz-1]){
                        t.emplace_back(nums[i]);
                        backtrack(t,i+1);
                        t.pop_back();
                    }
                }
            }
        };
        vector<ll>q;
        backtrack(q,0);
        return cnt;
    }
};

以等差数列的最后两元素为状态dp

dp[i][j] 表示以 i 和 j 下标对应最后两元素的等差数列个数,所以存在转移关系:dp[i][j] += dp[j][k]+1,(k<j<i);
而k是怎么来的呢?
nums[j] - nums[k] = nums[i] - nums[j] => nums[k] = 2*nums[j]-nums[i]
一旦存在这样的下标 k 便可进行状态转移

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        using ll = long long;
        int n = nums.size();
        if(n<3)return 0;
        unordered_map<ll,vector<int>>check;
        //记录值对应的下标,由于存在重复,所以用数组存
        for(int i=0;i<nums.size();i++){
            check[nums[i]].emplace_back(i);
        }
        int dp[n][n];
        int res = 0;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                ll target = (ll)2*nums[j]-nums[i];
                vector<int>& t = check[target];
                for(auto&& k:t){
                    if(k<j)
                        dp[i][j] += (dp[j][k]+1);
                }
                res += dp[i][j];
            }
        }
        return res;
    }
};

以上是关于等差数列划分--子序列问题DP解决的主要内容,如果未能解决你的问题,请参考以下文章

leetcode打卡——等差数列题目(LIS变式)——1218. 最长定差子序列

LeetCode 446 等差数列划分II - 子序列[动态规划] HERODING的LeetCode之路

LeetCode 446. 等差数列划分 II - 子序列(动态规划)/ 416. 分割等和子集 (背包问题)/322. 零钱兑换(完全背包)

2022-02-06:等差数列划分 II - 子序列。 给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。 如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称

经典DP问题之最长上升子序列和最长公共子序列

最长斐波那契子序列选取(离散化 + 二分 + DP)