从动态规划到贪心算法:最长递增子序列问题的方法全解析

简介: 从动态规划到贪心算法:最长递增子序列问题的方法全解析

题型简介

经典例题:300. 最长递增子序列 - 力扣(LeetCode)

最长递增子序列(Longest Increasing subsequence,LIS)是一个经典的问题。最长递增子序列是指在一个序列中,以不下降的顺序连续排列的一系列元素的子序列。这个子序列的长度就是最长递增子序列的长度。

题解代码

虽然注释详细,但与后文解题思路对应食用风味更佳~

#include <iostream>
#include <vector>
 
using namespace std;
 
int lengthOfLIS(vector<int>& nums) 
{
    // 如果输入序列为空,返回 0
    if (nums.empty()) 
    {
        return 0;
    }
 
    // 定义 dp 数组,长度为输入序列的长度
    int dp[nums.size()];
    // 初始化 dp 数组,将所有元素初始化为 1
    for (int i = 0; i < nums.size(); i++) 
    {
        dp[i] = 1;
    }
 
    // 记录最长递增子序列的长度
    int maxn = 1;
 
    // 遍历输入序列,从第 2 个元素开始,因为第一个元素的 dp[0] 一定是 1
    for (int i = 1; i < nums.size(); i++) 
    {
        // 遍历之前的元素,找到满足条件的索引 j
        for (int j = 0; j < i; j++) 
        {
            // 如果当前元素小于之前的元素,并且之前元素的最长递增子序列长度加 1 大于当前元素的最长递增子序列长度
            if ((nums[j] < nums[i]) && (dp[j] + 1 > dp[i])) 
            {
                // 更新当前元素的最长递增子序列长度为之前元素的最长递增子序列长度加 1
                // 因为if条件是nums[j] < nums[i],所以当前i位置的num一定是可以往j位置的数字后拼接作为递增子序列的
                // 所以更新当前i的dp作为新的当前dp[i]
                dp[i] = dp[j] + 1;
            }
        }
 
        // 在与每次遍历完当前i的j后更新的dp[i]与之前的maxn作对比
        // 得到当前最长递增子序列的长度
        if (dp[i] > maxn) 
        {
            maxn = dp[i];
        }
    }
 
    // 返回最长递增子序列的长度
    return maxn;
}
 
int main() 
{
    vector<int> nums = { 10, 9, 2, 5, 3, 7, 101, 18 };
    // 输出:4
    cout << lengthOfLIS(nums) << endl;
 
    return 0;
}

解题思路

1. 贪心策略(Greedy algorithms):

贪心算法的核心是以少博多,以最优解为目标

贪心策略是选择当前未处理元素中最小的元素,将其添加到最长递增子序列的末尾。这种策略的基本思想是尽可能地选择较小的元素,以保证子序列的递增性。

在代码中,我们通过比较当前元素 nums[i] 和之前元素 nums[j]j < i)的大小来更新最长递增子序列的长度。如果 nums[j] < nums[i],并且 dp[j] + 1 > dp[i],我们就选择 nums[j] 作为最长递增子序列的一部分,并更新 dp[i]dp[j] + 1

2. 动态规划(Dynamic programming):

动态规划是一种通过将问题分解为子问题来解决问题的方法。在最长递增子序列问题中,动态规划的基本思想是通过递推公式来计算每个元素的最长递增子序列长度。

在代码中,我们使用了一个长度为 nums.size() 的数组 dp 来存储每个元素的最长递增子序列长度。递推公式为 dp[i] = max(dp[j] + 1, dp[i]),其中 j < i 表示之前的元素。通过递推公式,我们可以逐步计算出每个元素的最长递增子序列长度。

剔骨刀(精细点)

    for (int i = 1; i < nums.size(); i++) 
    {
        for (int j = 0; j < i; j++) 
        {
            if ((nums[j] < nums[i]) && (dp[j] + 1 > dp[i])) 
            {
                dp[i] = dp[j] + 1;
            }
        }
 
        if (dp[i] > maxn) 
        {
            maxn = dp[i];
        }
    }

动态规划问题难点在于它的递推公式理解。

这里的 (nums[j] < nums[i]) && (dp[j] + 1 > dp[i]) 中的 dp[j] 可以当做前面已经在该下标上取得的最长递增子序列的个数,因为if条件(nums[j] < nums[i]) && (dp[j] + 1 > dp[i]),当条件通过时说明当前 i 位置的num一定是可以往j位置的数字后拼接作为递增子序列的,所以dp[j] + 1的意思就是说,只要在if条件内他都可以拼接,但是如果dp[j] + 1都小于dp[i]的话,那么它就不是最长子序列了,不会进行 +1 ,保留原来的 dp[i] 大小。  



目录
相关文章
|
6天前
|
存储 监控 算法
解析公司屏幕监控软件中 C# 字典算法的数据管理效能与优化策略
数字化办公的时代背景下,企业为维护信息安全并提升管理效能,公司屏幕监控软件的应用日益普及。此软件犹如企业网络的 “数字卫士”,持续记录员工电脑屏幕的操作动态。然而,伴随数据量的持续增长,如何高效管理这些监控数据成为关键议题。C# 中的字典(Dictionary)数据结构,以其独特的键值对存储模式和高效的操作性能,为公司屏幕监控软件的数据管理提供了有力支持。下文将深入探究其原理与应用。
22 4
|
15天前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
34 2
|
1月前
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
45 4
|
1月前
|
存储 监控 算法
员工电脑监控场景下 Python 红黑树算法的深度解析
在当代企业管理范式中,员工电脑监控业已成为一种广泛采用的策略性手段,其核心目标在于维护企业信息安全、提升工作效能并确保合规性。借助对员工电脑操作的实时监测机制,企业能够敏锐洞察潜在风险,诸如数据泄露、恶意软件侵袭等威胁。而员工电脑监控系统的高效运作,高度依赖于底层的数据结构与算法架构。本文旨在深入探究红黑树(Red - Black Tree)这一数据结构在员工电脑监控领域的应用,并通过 Python 代码实例详尽阐释其实现机制。
45 6
|
22天前
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
35 0
|
2月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于模糊神经网络的金融序列预测算法matlab仿真
本程序为基于模糊神经网络的金融序列预测算法MATLAB仿真,适用于非线性、不确定性金融数据预测。通过MAD、RSI、KD等指标实现序列预测与收益分析,运行环境为MATLAB2022A,完整程序无水印。算法结合模糊逻辑与神经网络技术,包含输入层、模糊化层、规则层等结构,可有效处理金融市场中的复杂关系,助力投资者制定交易策略。
|
2月前
|
监控 算法 安全
基于 C# 的内网行为管理软件入侵检测算法解析
当下数字化办公环境中,内网行为管理软件已成为企业维护网络安全、提高办公效率的关键工具。它宛如一位恪尽职守的网络守护者,持续监控内网中的各类活动,以确保数据安全及网络稳定。在其诸多功能实现的背后,先进的数据结构与算法发挥着至关重要的作用。本文将深入探究一种应用于内网行为管理软件的 C# 算法 —— 基于二叉搜索树的入侵检测算法,并借助具体代码例程予以解析。
56 4
|
2月前
|
JSON 监控 网络协议
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。
|
2月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
81 1
|
2月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

热门文章

最新文章

推荐镜像

更多