《算法题讲解指南:动态规划算法--子数组系列》--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数组分别记录上升和下降状态的最长子数组长度,根据元素大小关系进行状态更新。希望大家能有所收获!

相关推荐
消失的旧时光-19438 小时前
C++ 网络服务端主线:从线程池到 Reactor 的完整路线图
开发语言·网络·c++·线程池·并发
小O的算法实验室8 小时前
2026年SEVC,高密度仓库中结合任务分配的多AGV无冲突调度框架,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
abant28 小时前
leetcode 108 有序数组转平衡二叉树
算法·leetcode·职场和发展
汀、人工智能9 小时前
[特殊字符] 第7课:移动零
数据结构·算法·数据库架构·图论·bfs·移动零
计算机安禾9 小时前
【数据结构与算法】第25篇:静态查找(一):顺序查找与折半查找
java·开发语言·数据结构·学习·算法·visual studio code·visual studio
cookies_s_s9 小时前
C++ 模板与泛型编程
linux·服务器·开发语言·c++
2401_892070989 小时前
【Linux C++ 日志系统实战】Logger 日志器完整实现:级别控制、宏封装、动态输出、自动崩溃退出
linux·c++·日志系统
B1acktion9 小时前
2.7.希尔排序——让插入排序先大步走,再小步收尾
c++·算法·排序算法
原来是猿9 小时前
Linux进程信号详解(一):信号快速认识
linux·c++·算法