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;以确保编译出的代码能够在硬件上正常运行。 添…

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

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

大唐杯学习笔记: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;首次登顶年度…

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

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

多模态入门

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 实例上。 网…

3.6作业

作业要求&#xff1a;数据库操作的增、删、改 程序代码&#xff1a; #include<myhead.h> int main(int argc, const char *argv[]) {//定义数据库句柄指针sqlite3 * ppDb NULL;//打开数据库&#xff0c;如果数据库不存在&#xff0c;则创建数据库//将数据库句柄由参数…

【Leetcode每日一刷】数组|704. 二分查找、27. 移除元素

力扣每日刷题 一、704. 二分查找1.1、题目1.2、解题思路1.3、代码实现——C1.4、 总结&易错 二、27. 移除元素2.1&#xff1a;题目2.2、解题思路2.3、代码实现——C1.4、 总结&易错 一、704. 二分查找 1.1、题目 704. 二分查找 1.2、解题思路 题型&#xff1a;数组…

2024年洗地机推荐,希亦、美的、西屋、顺造洗地机哪个品牌最耐用质量好?

对许多人来说&#xff0c;全屋清洁可能是件让人望而却步的任务&#xff0c;因为它需要花费大量的体力和时间。但是&#xff0c;随着科技的发展&#xff0c;我们可以找到一些能够简化这个过程的神器&#xff0c;比如洗地机。有了洗地机&#xff0c;我们可以轻松地完成扫地、拖地…

【CSP试题回顾】201509-1-数列分段

CSP-201509-1-数列分段 解题代码 #include <iostream> #include <vector> #include <algorithm> using namespace std;int n, t, maxSeg 0;int main() {cin >> n;vector<int>list(n);for (int i 0; i < n; i){cin >> list[i];}auto…

JavaBoy假期如何学习项目?弯道块才是真的快!

至暗时刻 老话说的好&#xff0c;弯道快才是真的快&#xff0c;谁直线不会加油&#xff1f;每到假期都是在座的各位弯道超车的时候。转眼自己已经出来搬了快四年砖头了&#xff0c;偶尔访问下牛客发现行情真是一年不如一年。。。不由得回想起自己春招时候的经历。 回想起2020年…

HI3519DV500 HI3519DRFCV500 HI3519DRBCV500 海思安防监控芯片 提供原厂开发包

一、总体介绍 Hi3519DV500是一颗面向视觉行业推出的超 高清智能 SoC。该芯片最高支持四路sensor输 入&#xff0c;支持最高4K30fps的ISP图像处理能力&#xff0c;支持 2F WDR、多级降噪、六轴防抖、全景拼接、多光 谱融合等多种传统图像增强和处理算法&#xff0c;支持通过AI…

浏览器发出一个请求到收到响应步骤详解

前言 在网络通信中&#xff0c;浏览器向Web服务器发送HTTP请求消息的过程是一个复杂而精密的环节&#xff0c;涉及到URL解析、DNS解析、数据拆分、路由表规则和MAC头部添加等一系列步骤。本文将深入探讨这一过程的每个环节&#xff0c;帮助读者更全面地了解浏览器与Web服务器之…

双体系Java学习之关键字,标识符以及命名规范

重新开始从Java基础开始学&#xff0c;保持每周两更的状态&#xff0c;刚开学事情有点多。 关键字 标识符 命名规范