2024/3/5打卡最长上升子序列**----线性DP,贪心,单调栈

目录

题目:

DP分析:

代码:

3.6更新 贪心 第一个思考方式

先上代码:

解析:

贪心 第二个思考方式 (与上面的思路差不多,但是换了个角度)

思路:

代码:


 所有的思路很重要!!!

题目:

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000,
−10^9≤数列中的数≤10^9

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

DP分析:

代码:

import java.io.*;
import java.util.*;class Main{static int N = 1010;static int[] w = new int[N];static int[] f = new int[N];public static void main(String[] args) throws IOException{BufferedReader in = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(in.readLine()); String[] s = in.readLine().split(" ");for(int i=1;i<=n;i++){w[i] = Integer.parseInt(s[i-1]);}// DPfor(int i=1;i<=n;i++){f[i] = 1; // 只有自身for(int j=1;j<i;j++){if(w[j]<w[i]) // 保证序列单调上升f[i] = Math.max(f[i],f[j]+1);}}// 不一定f[n]就是最长的,因为f[n]必定包含w[n]这个数,最长上升子序列可能不包含w[n]int res = 0;for(int i=1;i<=n;i++) res = Math.max(res,f[i]);System.out.println(res);}
}

 PS:

本来想尝试单调栈的。发现比如  3 1 2 1 8 5 6 。栈会删去 2 ,存进 1。而最长上升子序列为 1 2 5 6。


3.6更新 贪心 第一个思考方式

        上面说到不可以使用单调栈。并且使用上面的代码,时间复杂度是O(n^2),如果N增大,就会超时。

单调栈:单调栈(思路+示例)-CSDN博客

但是参考了一篇大佬的代码 AcWing 896. 最长上升子序列 II - AcWing  我悟了。

先上代码:

// 类似于单调栈的做法,但是更多的有贪心的思想在。
// 替换掉第一个大于等于该值的在栈中的元素,目的是保证可以最大限度的增加上升子序列的长度。
/* ex:3 1 2 1 8 5 6使用st[]存储值i = 1, st[] = {3}i = 2, st[] = {1}i = 3, st[] = {1,2}i = 4, st[] = {1,2} 此时这个1是被w[4]替换掉w[2]的1i = 5, st[] = {1,2,8}i = 6, st[] = {1,2,5}i = 7, st[] = {1,2,5,6}
*/import java.io.*;
import java.util.*;class Main{static int N = 100010;static int[] w = new int[N];static int[] st = new int[N]; // 单调栈public static void main(String[] args) throws IOException{BufferedReader in = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(in.readLine()); String[] s = in.readLine().split(" ");for(int i=1;i<=n;i++){w[i] = Integer.parseInt(s[i-1]);}int res = 0;int tt = 0; // 栈顶for(int i=1;i<=n;i++){if(tt==0||w[i]>st[tt]){ // 如果栈中没有值或者当前值大于栈顶元素,加入进去st[++tt] = w[i];   res = Math.max(res,tt);}else{ // 否则,替换掉第一个大于等于该值的在栈中的元素int idx = tt;while(idx>0&&w[i]<=st[idx])idx--;st[idx+1] = w[i];}}System.out.println(res);}
}

解析:

        使用了单调栈+贪心的思想。

        如果当前该值 w[i] 大于栈顶元素,就加入栈 st[ \ ]进去。

        否则,找到栈中第一个大于等于 w[i] 的值,用 w[i] 替换掉该值。

                                                                                (用二分,虽然这个代码没用)

ex:

         a=[3,1,4,1,5,9,2]
         i = 1, st[\ ] = {3}\\ i = 2, st[\ ] = {1}\\ i = 3, st[\ ] = {1,4}\\ i = 4, st[\ ] = {1,4} \\ i = 5, st[\ ] = {1,4,5}\\ i = 6, st[\ ] = {1,4,5,9}\\ i = 7, st[\ ] = {1,2,5,9}\\

        疑惑:

  1. 一般的单调栈会将第 4 步的时候,删去 1,4 ,再将 a[4] 装入进去,但是会缩短上升子序列的长度。并且在 7 步的时候,数字 9 被保留了下来,没有被去除换成 2
  2. 最后的序列为 (1,2,5,9),而准确的最长上升子序列应该是 ({1,4,5,9}),这是为什么。

        第一个问题:为什么有这种替换操作?主要是贪心,我们并不想在求解的过程中导致最长上升子序列越算越短。因此,如果我们目前算出的结果还没以前的长,会暂时保留以前的结果,当然也不丢弃目前的结果,因为之后继续计算的话,目前的结果可能更优。

        为了实现上述目的,我们可以用新序列从左到右逐渐覆盖掉旧序列。当新序列长度 <原序列长度时,原序列没有被完全覆盖,因此保证长度不减小;当新序列长度 ≥原序列长度时,原序列已经被完全覆盖,现在就是以新序列为基础进行计算了。

        因此就产生了这种特殊的替换方式。

        第二个问题最后的最长上升子序列不是准确的?因为由于贪心的思想,存在这种替换方式,导致最长上升子序列中某些值被替换掉,但是上升子序列的长度没有发生改变,只有里面的值发生了变化,因此,在求解该题中最长上升子序列的长度时可以被使用,但是要输出最长上升子序列的值的时候,就不行了。

        核心就是因为栈中储存的不只有一个序列,是旧序列和新序列合并的产物,因此不一定是最终最长上升子序列。

 


贪心 第二个思考方式 (与上面的思路差不多,但是换了个角度)

思路:

        换一种思路思考贪心的问题,如果我们要将 w[i] 作为上升子序列的末尾元素。那么我们可以设计一个数组 q[\ ],存储当上升子序列长度分别为 [1,2,3...\ n] 的时候最小的末尾值,其中,末尾值都是跟随数组下标的上升而严格上升的

        如果想要将 w[i] 装入到序列中,我们只需要在长度为 q[\ ] 中找到大于等于 w[i] 的第一个末尾值 q[j] ,此时 q[j-1] < w[i],\ q[j]>=w[i] 。此时我们将 w[i] 装入到 q[j-1] 的末尾,此时,序列的长度就增加了1,因此就要更新 q[j] 的末尾值为 w[i] (这个操作就跟上面那种方法替换大于等于 w[i] 的第一个值思路一样),从而保证了序列中的值尽可能小来增加最长子序列的可能性。

        因为数组 q[\ ] 中的值是严格单调递增的,因此可以使用二分来找到该 j 点。

 

代码:

import java.io.*;
import java.util.*;class Main{static int N = 100010;static int[] w = new int[N];static int[] q = new int[N]; // 存储每个上升子序列长度的最小末尾值public static void main(String[] args) throws IOException{BufferedReader in = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(in.readLine()); String[] s = in.readLine().split(" ");for(int i=1;i<=n;i++){w[i] = Integer.parseInt(s[i-1]);}int len = 0; // q数组的长度for(int i=1;i<=n;i++){int l = 0, r = len; // l只能从0开始,因为存值的时候要+1while(l<r){ // 找到q[l]<w[i]<=q[l+1] 的l点int mid = l+r+1>>1;if(q[mid]<w[i]) l = mid;else r = mid-1;}q[r+1] = w[i]; // 更新l+1的末尾值len = Math.max(len,r+1); // 取当前数组q和更新的数组q长度的最大值}System.out.println(len);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/724316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

freeRTOS day1

总结keil5下载代码和编译代码需要注意的事项 选择合适的微控制器型号&#xff1a;确保你选择的控制器型号与你的项目中实际使用的硬件相匹配。 配置项目设置&#xff1a;正确设置目标芯片的时钟频率、内存大小等参数&#xff0c;以确保编译出的代码能够在硬件上正常运行。 添…

qt 语音引擎 QTextToSpeech Microsoft SAPI

QT中语音播报的代码 在QT中实现语音播报可以使用QTextToSpeech类&#xff0c;具体代码如下&#xff1a; #include <QCoreApplication> #include <QTextToSpeech> #include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 创…

数据结构->链表分类与oj(题),带你提升代码好感

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;橘橙黄又青-CSDN博客 1.&#x1f34e;链表的分类 前面我们学过顺序表&#xff0c;顺序表问题&#xff1a; …

程序员如何选择职业赛道

程序员如何选择职业赛道&#xff1f; 程序员的职业赛道就像是一座迷宫&#xff0c;有前端的美丽花园&#xff0c;后端的黑暗洞穴&#xff0c;还有数据科学的神秘密室。你准备好探索这个充满挑战和机遇的迷宫了吗&#xff1f;快来了解如何选择职业赛道吧&#xff01; 方向一&a…

大唐杯学习笔记:Day5

1.1 小区搜索 搜索流程 PLMN选择 自动模式&#xff1a;UE根据NAS的请求或自主地向NAS报告可用的PLMN 手动模式&#xff1a;通过手动选择一个可用的VPLMN获取正常服务 频点选择 5G NR中,3GPP主要指定了两个频率范围,一个是6GHZ以下,另一个是毫米波,分别称之为FR1和FR2。 N…

AIOps实践中常见的挑战:故障根因与可观测性数据的割裂

运维的挑战与责任 在数字化时代&#xff0c;运维团队面临的挑战前所未有。他们不仅要确保系统的高可用性和高性能&#xff0c;还要快速响应并解决故障&#xff0c;以减少对业务的影响。在这种背景下&#xff0c;运维团队急需工具和技术&#xff0c;能够帮助他们提高效率&#…

一文解释python中的实例方法,类方法和静态方法作用和区别是啥?该如何使用

我们都知道 &#xff0c;python类中有三种常见的方法 &#xff0c;分别是实例方法 &#xff0c;类方法和静态方法 。那么这几个方法到底有什么作用 &#xff1f; 它们之间有什么区别 &#xff1f;该如何使用 &#xff1f; 带着这些问题 &#xff0c;下面我们就来了解下这三种方…

1688商品详情数据采集,工程数据采集丨店铺数据采集丨商品详情数据采集

1688是中国的一个大型B2B电子商务平台&#xff0c;主要用于批发和采购各种商品。对于需要从1688上获取商品详情数据、工程数据或店铺数据的用户来说&#xff0c;可以采用以下几种常见的方法&#xff1a; 官方API接口&#xff1a;如果1688提供了官方的API接口&#xff0c;那么可…

【高效开发工具系列】vimdiff简介与使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

米哈游排名首超腾讯,登顶榜首 !!!

米哈游排名首超腾讯&#xff0c;登顶榜首 &#xff01;&#xff01;&#xff01; 大家好&#xff0c;我是銘&#xff0c;全栈开发程序员。 近日&#xff0c;第三方机构 data.ai 公布 2023 年中国游戏厂商及应用出海收入 30 强。 其中米哈游超越腾讯&#xff0c;首次登顶年度…

我的知识脉络

O、自我介绍 一、技术选型 前端框架&#xff1a;vue2、vue3、React 老版本及 hooks版本&#xff1b; SSR框架&#xff1a;next&#xff1b; 微前端框架&#xff1a;Single-SPA、qiankun&#xff08;乾坤&#xff09;、无界、McroApp&#xff1b; 跨端方案&#xff1a;RN、webA…

为何禁止将控件指针传入子线程进行更新?

在Qt中&#xff0c;直接在子线程中更新GUI控件是不安全的&#xff0c;也不被允许。Qt的GUI部分是非线程安全的&#xff0c;这意味着所有的GUI相关操作都应该只在主线程&#xff08;也就是GUI线程&#xff09;中执行。尝试在子线程中访问或修改GUI控件可能会导致不可预测的行为和…

深度解析人工智能领域的迁移学习技术

摘要&#xff1a; 迁移学习是人工智能领域中的一个重要分支&#xff0c;它使计算机能够将在一个任务上学到的知识应用到其他任务上。本文将深入探讨迁移学习技术&#xff0c;包括其原理、技术和应用&#xff0c;并通过丰富的案例分析展示其在实际场景中的应用。 引言&#xf…

Selenum八种常用定位(案例解析)

Selenium是一个备受推崇的工具。它有着丰富的功能&#xff0c;让我们能够与网页互动&#xff0c;执行各种任务&#xff0c;能为测试工程师和开发人员提供了很大的便利。 要充分利用Selenium&#xff0c;就需要了解如何正确定位网页上的元素。 接下来我将带大家共同探讨Seleni…

【js】数组的常用方法

增加 push,unshift,splice,concat 前面三种修改原数组,concat不会修改原数组push 从后面添加数据,并返回新数组的长度unshift 从前面添加数据,并返回新数组的长度splice 可以接受三个参数,第一个参数开始位置,第二个参数是删除元素的数量,第三个参数是插入的数据concat 合并数…

多模态入门

VIT处理图像 CNN VS Transformer 多模态BLIP模型 网络结构 视觉编码器: 就是 ViT 的架构。将输入图像分割成一个个的 Patch 并将它们编码为一系列 Image Embedding,并使用额外的 [CLS] token 来表示全局的图像特征。视觉编码器不采用之前的基于目标检测器的形式,因为 ViLT 和…

推荐书籍《低代码平台开发实践:基于React》—— 提升开发效率,构建优质应用

写在前面 随着数字化转型的深入&#xff0c;企业对应用开发效率和灵活性的要求不断提高。低代码平台作为新兴的软件开发方式&#xff0c;通过可视化界面和预构建组件&#xff0c;极大简化了应用开发流程&#xff0c;降低了技术门槛。基于React的低代码平台以其组件化、响应式和…

Kube-Prometheus 监控Istio

推荐 Istio 多集群监控使用 Prometheus&#xff0c;其主要原因是基于 Prometheus 的分层联邦&#xff08;Hierarchical Federation&#xff09;。 通过 Istio 部署到每个集群中的 Prometheus 实例作为初始收集器&#xff0c;然后将数据聚合到网格层次的 Prometheus 实例上。 网…

Effective C++ 学习笔记 条款14 在资源管理类中小心copying行为

条款13导入这样的观念&#xff1a;“资源取得时机便是初始化时机”&#xff08;Resource Acquisition Is Initialization&#xff0c;RAII&#xff09;&#xff0c;并以此作为“资源管理类”的脊柱&#xff0c;也描述了auto_ptr和tr1::shared_ptr如何将这个观念表现在heap-base…

PAT知识点——python保留小数点后两位的操作

python保留小数点后两位 在Python中&#xff0c;可以使用几种不同的方法来保留小数点后两位。 使用round()函数&#xff1a; num 3.14159 rounded_num round(num, 2) print(rounded_num) # 输出&#xff1a;3.14使用字符串格式化操作符 %&#xff1a; num 3.14159 …