《算法题讲解指南:动态规划算法--子数组系列》--23.等差数列划分,24.最长湍流子数组

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》
《算法题讲解指南》--优选算法
《算法题讲解指南》--递归、搜索与回溯算法
《算法题讲解指南》--动态规划算法

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

23.等差数列划分

题目链接:

题目描述:

题目示例:

解法(动态规划):

算法思路:

C++算法代码:

算法总结及流程解析:

24.最长湍流子数组

题目链接:

题目描述:

题目示例:

解法(动态规划):

算法思路:

C++算法代码:

算法总结及流程解析:

结束语


23.等差数列划分

题目链接:

413. 等差数列划分 - 力扣(LeetCode)

题目描述:

题目示例:

解法(动态规划):

算法思路:

1.状态表示:

由于我们的研究对象是「一段连续的区间」,如果我们状态表示定义成[0,i]区间内一共有多少等差数列,那么我们在分析dp[i]的状态转移时,会无从下手,因为我们不清楚前面那么多的「等差数列都在什么位置」。所以说,我们定义的状态表示必须让等差数列「有迹可循」,让状态转移的时候能找到「大部队」。因此,我们可以「固定死等差数列的结尾」,定义下面的状态表示:

dp[i] 表示:必须「以i位置的元素为结尾」的等差数列有多少种。

2.状态转移方程:

我们需要了解一下等差数列的性质:如果abc三个数成等差数列,这时候来了一个d,其中bcd也能构成一个等差数列,那么abcd四个数能够成等差序列吗?答案是:显然的。因为他们之间相邻两个元素之间的差值都是一样的。有了这个理解,我们就可以转而分析我们的状态转移方程了。

对于dp[i]位置的元素nums[i],会与前面的两个元素有下面两种情况:

i.nums[i-2],nums[i- 1],nums[i]三个元素不能构成等差数列:那么以nums[i]为结尾的等差数列就不存在,此时dp[i]=;

ii.nums[i- 2],nums[i- 1],nums[i]三个元素可以构成等差数列:那么以nums[i-1]为结尾的所有等差数列后面填上一个 nums[i]也是一个等差数列,此时dp[i]=dp[i-1] 。但是,因为nums[i-2],nums[i-1],nums[i]三者又能构成一个新的等差数列,因此要在之前的基础上再添上一个等差数列,于是dp[i]= dp[i- 1] + 1。

综上所述:状态转移方程为:

当:nums[i-2] + nums[i] != 2 * nums[i - 1] 时, dp[i] = 0

当:nums[i -2] + nums[i] ==2 * nums[i - 1] 时,dp[i]= 1 + dp[i-1]

3.初始化:

由于需要用到前两个位置的元素,但是前两个位置的元素又无法构成等差数列,因此dp[0] = dp[1] = 0。

4.填表顺序:

毫无疑问是「从左往右」。

5.返回值:

因为我们要的是所有的等差数列的个数,因此需要返回整个 dp表里面的元素之和。

C++算法代码:

cpp 复制代码
class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) 
    {
        int n = nums.size();
        if(n == 1 || n == 2)
        {
            return 0;
        }
        vector<int> dp(n);
        dp[0] = dp[1] = 0;

        for(int i = 2; i < n; i++)
        {
            if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2])
            {
                dp[i] = dp[i - 1] + 1;
            }
            else
            {
                dp[i] = 0;
            }
        }
        int ret = 0;
        for(int i = 0; i < n; i++)
        {
            ret += dp[i];
        }
        return ret;
    }
};

算法总结及流程解析:

24.最长湍流子数组

题目链接:

978. 最长湍流子数组 - 力扣(LeetCode)

题目描述:

题目示例:

解法(动态规划):

算法思路:

1.状态表示:

我们先尝试定义状态表示为:

dp[i]表示「以i位置为结尾的最长湍流数组的长度」。

但是,问题来了,如果状态表示这样定义的话,以i位置为结尾的最长湍流数组的长度我们没法从之前的状态推导出来。因为我们不知道前一个最长湍流数组的结尾处是递增的,还是递减的。因

此,我们需要状态表示能表示多一点的信息:要能让我们知道这一个最长湍流数组的结尾是「递

增」的还是「递减」的。

因此需要两个dp表:

f[i]表示:以i位置元素为结尾的所有子数组中,最后呈现「上升状态」下的最长湍流数组的长度;

g[i]表示:以i位置元素为结尾的所有子数组中,最后呈现「下降状态」下的最长湍流数组的长度。

2.状态转移方程:

对于i位置的元素arr[i],有下面两种情况:

i.arr[i]>arr[i-1]:如果i位置的元素比i-1位置的元素大,说明接下来应该去找i -1 位置结尾,并且i-1 位置元素比前一个元素小的序列,那就是g[i-1]。更新 f[i]位置的值:f[i]=g[i-1]+ 1;

ii.arr[i]< arr[i-1]:如果i位置的元素比i-1 位置的元素小,说明接下来应该去找i- 1 位置结尾,并且i-1 位置元素比前一个元素大的序列,那就是f[i-1]。更新 g[i]位置的值:g[i] = f[i-1] + 1 ;

iii.arr[i]==arr[i -1]:不构成湍流数组。

3.初始化:

所有的元素「单独」都能构成一个湍流数组,因此可以将dp表内所有元素初始化为1。

由于用到前面的状态,因此我们循环的时候从第二个位置开始即可。

4.填表顺序:

毫无疑问是「从左往右,两个表一起填」。

5.返回值:

应该返回「两个dp表里面的最大值」,我们可以在填表的时候,顺便更新一个最大值。

C++算法代码:

cpp 复制代码
class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) 
    {
        int n = arr.size();
        if(n == 1)
        {
            return 1;
        }
        vector<int> dp(n);
        dp[0] = 1;
        dp[1] = arr[0] == arr[1] ? 1 : 2;
        for(int i = 2; i < n; i++)
        {
            if(arr[i] > arr[i - 1])
            {
                dp[i] = arr[i - 1] < arr[i - 2] ? dp[i - 1] + 1 : 2;
            }
            else if(arr[i] < arr[i - 1])
            {
                dp[i] = arr[i - 1] > arr[i - 2] ? dp[i - 1] + 1 : 2;
            }
            else
            {
                dp[i] = 1;
            }
        }    
        int ret = INT_MIN;
        for(int i = 0; i < n; i++)
        {
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

算法总结及流程解析:

结束语

到此,23.等差数列划分,24.最长湍流子数组 这两道算法题就讲解完了。等差数列划分问题:通过定义dp[i]表示以i结尾的等差子数组数量,利用相邻元素差值关系进行状态转移;最长湍流子数组问题:采用双DP数组分别记录上升和下降状态的最长子数组长度,根据元素大小关系进行状态更新。希望大家能有所收获!

相关推荐
代码中介商几秒前
C++ STL 容器完全指南(二):vector 深入与 stringstream 实战
开发语言·c++
YUDAMENGNIUBI1 小时前
day20_逻辑回归
算法·机器学习·逻辑回归
澈2075 小时前
C++并查集:高效解决连通性问题
java·c++·算法
郝学胜-神的一滴6 小时前
Qt 入门 01-01:从零基础到商业级客户端实战
开发语言·c++·qt·程序人生·软件构建
宏笋6 小时前
C++ thread的detach()方法详解
c++
旖-旎6 小时前
深搜练习(单词搜索)(12)
c++·算法·深度优先·力扣
企客宝CRM7 小时前
2026年中小企业CRM选型指南:企客宝CRM处于什么位置?
android·算法·企业微信·rxjava·crm
橙淮7 小时前
二叉树核心概念与Java实现详解
数据结构·算法
大卡片8 小时前
C++的基础知识点
开发语言·c++
米罗篮8 小时前
DSU并查集 & 拓展欧几里得-逆元
c++·经验分享·笔记·算法·青少年编程