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

news2024/11/26 4:29:08

目录

题目:

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]);
        }
        
        // DP
        for(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]的1
    i = 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开始,因为存值的时候要+1
            while(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.coloradmin.cn/o/1494135.html

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

相关文章

探索神经网络在商品销售和图像识别中的应用

目录 前言人工神经网络简介1.1 人工神经网络与深度学习简介1.2 生物神经元结构与神经元机器模型1.3 神经网络的数据量与性能关系 2 需求预测2.1 需求预测的背景2.2 商品销售神经元机器模型2.3 多层神经网络结构的优势 3 图像识别3.1 图像识别神经网络技术3.2 实际应用场景 结语…

AI领域再出“王炸“----Claude3是否会成为下一个“神“

目录 一.Claude3最新发布 二.Claude3支持20万token 三.Claude3在未公开算法上取得重大突破 1.Claude 3读懂博士论文 2.量子跃迁集成&#xff1a; Claude 3智商&#xff1a;101 测试方法 测试细节 通过Karpathy挑战 Claude 3自画像&#xff0c;突破本我 从洛杉矶排到…

3_1储能容量配置

% 创建优化问题 problem optimproblem(ObjectiveSense, minimize);% 定义变量 x optimvar(x, 2, 1, LowerBound, 0); % 储能设备容量变量% 定义目标函数 problem.Objective 2*x(1) 3*x(2); % 假设成本函数为2*x1 3*x2% 定义约束条件 problem.Constraints.capacity1 x(1) …

01-环境搭建、SpringCloud微服务(注册发现、服务调用、网关)

环境搭建、SpringCloud微服务(注册发现、服务调用、网关) 1)课程对比 2)项目概述 2.1)能让你收获什么 2.2)项目课程大纲 2.3)项目概述 随着智能手机的普及&#xff0c;人们更加习惯于通过手机来看新闻。由于生活节奏的加快&#xff0c;很多人只能利用碎片时间来获取信息&…

画图解题思路( ccf 201512-3)

分析 首先需要转换坐标系&#xff0c;可以将两个坐标系的点写出来&#xff0c;对比一下找规律 可以发现题目中的坐标(x, y)转变成数组坐标系为(n - y - 1, x); 然后再判断是画线还是填充 画线&#xff1a;先转换题目坐标&#xff0c;再遍历画线 填充&#xff1a;采用dfs

Vue中有哪些优化性能的方法?

Vue是一款流行的JavaScript框架&#xff0c;用于构建交互性强的Web应用程序。在前端开发中&#xff0c;性能优化是一个至关重要的方面&#xff0c;尤其是当应用程序规模变大时。Vue提供了许多优化性能的方法&#xff0c;可以帮助开发人员提升应用程序的性能&#xff0c;从而提升…

LABEL-EFFICIENT SEMANTIC SEGMENTATION WITHDIFFUSION MODELS

基于扩散模型的标签高效语义分割 摘要&#xff1a; 去噪扩散概率模型最近受到了很多研究的关注&#xff0c;因为它们优于gan等替代方法&#xff0c;并且目前提供了最先进的生成性能。扩散模型的优越性能使其成为一些应用程序的吸引人的工具&#xff0c;包括绘图&#xff0c;超…

【Python】3. 基础语法(2)

顺序语句 默认情况下, Python 的代码执行顺序是按照从上到下的顺序, 依次执行的. print("1") print("2") print("3")执行结果一定为 “123”, 而不会出现 “321” 或者 “132” 等. 这种按照顺序执行的代码, 我们称为 顺序语句. 这个顺序是很关…

使用php_screw实现PHP代码加密

一&#xff1a;php_screw下载地址 https://gitee.com/splot/php-screw-plus https://github.com/del-xiong/screw-plus 二&#xff1a;php_screw安装 1&#xff1a;解压并修改加密key unzip php-screw-plus-master.zip cd php-screw-plus-master 打开php-screw-plus-mast…

【Docker】技术架构演变

【Docker】技术架构演变 目录 【Docker】技术架构演变架构中的概念架构演进单机架构相关软件 应用数据分离架构应用服务集群架构相关软件 读写分离/主从分离架构相关软件 引入缓存——冷热分离架构相关软件 垂直分库&#xff08;分布式数据库架构&#xff09;相关软件 业务拆分…

力扣--动态规划516.最长回文子序列

思路分析&#xff1a; 创建一个二维动态规划表dp&#xff0c;其中dp[i][j]表示在子串s[i...j]中的最长回文子序列的长度。初始化基本情况&#xff1a;对角线上的元素dp[i][i]都为1&#xff0c;因为单个字符本身就是长度为1的回文子序列。从字符串末尾向前遍历&#xff0c;填充…

Java Day2 面向对象

这里写目录标题 1、static总结1.1 代码块1.1.1 静态代码块1.1.2 实例代码块1.1.3 小例子 2、继承2.1 权限修饰符2.2 方法重写2.3 子类访问成员特点2.4子类构造器的特点 3、多态4、final、常量4.1 final4.2 常量 5 抽象类5.1 概念5.2 模板设计方法 6、接口6.1 接口新方法6.2 接口…

Java工程师必备知识,系列教学

一、前言 在这里我不得不感慨Spring的代码的完善与优秀&#xff0c;从之前看源码迷迷糊糊到现在基本了解Spring的部分源码后&#xff0c;愈来愈发现Spring开发者的思虑之周全&#xff01; 之前说过学习源码的目的在哪&#xff1f;正如我特别喜欢的一句话&#xff0c;有道无术…

进口及国内细胞分析仪厂家名录大全-贝克曼、安捷伦、希森美康、迈瑞.....

流式细胞仪是一种测量层流中细胞的设备&#xff08;细胞仪&#xff09;&#xff0c;其通过将每个细胞排列在鞘液中&#xff0c;加以激光束照射&#xff0c;可测量散射光和荧光&#xff0c;从而获得有关每个细胞的信息&#xff0c;包含细胞结构&#xff08;如大小、粒度、表面积…

移动开发:网格视图

一、在新建GridView模块下添加图片以及创建cell.xml文件 1.粘贴图片时选择红框中的路径&#xff0c;点击“OK” 2.在路径后添加-mdpi后缀,再点击“OK” 二、相关代码块 1.MainActivity.java文件代码 package com.example.gridview;import androidx.appcompat.app.AppCompatAc…

指针的学习4

目录 回调函数 qsort使用样例 使用qsort函数排序整形数据 使用qsort函数排序结构体 回调函数 回调函数就是一个通过函数指针调用的函数。如果把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xf…

如何使用达摩盘

目录 1.定义 2.功能&#xff1a;圈人群、画像洞察、同步到站内渠道投放&#xff1b; 1.定义 是阿里妈妈基于商业化营销场景打造的人群精细化运营定向中台&#xff0c;涵盖消费行为、兴趣偏好、地理位置等海量数据标签&#xff0c;为商家提供个性化人群圈选&#xff0c;识别店…

入门指南:使用uni-app构建跨平台应用

入门指南&#xff1a;使用uni-app构建跨平台应用 &#x1f31f; 前言 欢迎来到我的小天地&#xff0c;这里是我记录技术点滴、分享学习心得的地方。&#x1f4da; &#x1f6e0;️ 技能清单 编程语言&#xff1a;Java、C、C、Python、Go前端技术&#xff1a;Jquery、Vue.js、R…

WebGIS开发0基础必看教程:矢量查询

1.前言 在第七章里我们知道了WebGIS中要素的本质是UIComponent&#xff0c;而矢量图层的本质是包含了n&#xff08;n>0&#xff09;个UIComponent的Canvas。我们在UIComponent的graphics中&#xff0c;根据矢量数据画出矢量的形状(shape)&#xff0c;并且将矢量数据的属性(…

C及C++每日练习(1)

一.选择&#xff1a; 1.以下for循环的执行次数是&#xff08;&#xff09; for(int x 0, y 0; (y 123) && (x < 4); x); A.是无限循环 B.循环次数不定 C.4次 D.3次 对于循环&#xff0c;其组成部分可以四个部分&#xff1a; for(初始化;循环进行条件;调整) …