牛客NC164 最长上升子序列(二)【困难 贪心+二分 Java/Go/PHP/C++】

题目

在这里插入图片描述
题目链接:
https://www.nowcoder.com/practice/4af96fa010c44638a7e112abf65f7237

思路

贪心+二分

    所谓贪心,就是往死里贪,所以对于最大上升子序列,结尾元素越小,越有利于后面接上其他的数,
    也就可能变得更长
    所以贪心的做法是,建立一个dp数组,dp[i[表示长度为i的LIS结尾元素的最小值,
    因此我们只需要维护dp数组即可

    对于每一个数组的数,我们对他们进行判断,如果他大于等于dp[ans]的值,就把他放在数组后面,
    dp[++ans] = tr[i],
    否则,就在dp中去找大一个大于等于他的位置pos,dp[pos] = tr[i]。如果从头扫一遍数组,
    时间复杂度还是O(n^2),
    这与曹贼何异?!通过观察我们知道,这次维护的dp数组是单调递增的,
    所以就可以使出秘技二分之lower_bound来找pos的位置
    我们举个栗子:

        tr[] = 2 5 18 3 4 7 10 9 11 8 15
        dp[1] = 2;
        5大于2,所以dp[2] = 5
        18大于5,所以dp[3] = 18
        3小于18,所以二分去找,pos是2,所以dp[2] = 3
        4小于18,所以二分去找,pos是3,所以dp[3] = 4
        7大于4,所以dp[4] = 7
        10大于7,所以dp[5] = 10
        9小于10,所以二分去找,pos是5,dp[5] = 9
        11大于9,所以dp[6] = 11
        8小于11,所以二分去找,pos是5,dp[5] = 8
        15大于11,所以dp[7] = 15

    所以最长上升子序列的长度为7
    注意:dp数组得到的不一定是真正的LIS
    比如:tr[] = 1 4 7 2 5 9 10 3
    得到的是1 2 3 9 10,而真正的LIS是1 2 5 9 10
    因此dp数组得到的不一定是真正的LIS,他只表示最长子序列长度的排好序的最小序列,
    对于最后一半将5换成3的意义是记录最小序列,便于后续数据的处理

Java代码

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 该数组最长严格上升子序列的长度
     * @param a int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] a) {
        //https://blog.csdn.net/weixin_51216553/article/details/114678534
        /*
        贪心+二分

        所谓贪心,就是往死里贪,所以对于最大上升子序列,结尾元素越小,越有利于后面接上其他的数,也就可能变得更长
        所以贪心的做法是,建立一个dp数组,dp[i[表示长度为i的LIS结尾元素的最小值,因此我们只需要维护dp数组即可

        对于每一个数组的数,我们对他们进行判断,如果他大于等于dp[ans]的值,就把他放在数组后面,dp[++ans] = tr[i],
        否则,就在dp中去找大一个大于等于他的位置pos,dp[pos] = tr[i]。如果从头扫一遍数组,时间复杂度还是O(n^2),
        这与曹贼何异?!通过观察我们知道,这次维护的dp数组是单调递增的,所以就可以使出秘技二分之lower_bound来找pos的位置
        我们举个栗子:

            tr[] = 2 5 18 3 4 7 10 9 11 8 15
            dp[1] = 2;
            5大于2,所以dp[2] = 5
            18大于5,所以dp[3] = 18
            3小于18,所以二分去找,pos是2,所以dp[2] = 3
            4小于18,所以二分去找,pos是3,所以dp[3] = 4
            7大于4,所以dp[4] = 7
            10大于7,所以dp[5] = 10
            9小于10,所以二分去找,pos是5,dp[5] = 9
            11大于9,所以dp[6] = 11
            8小于11,所以二分去找,pos是5,dp[5] = 8
            15大于11,所以dp[7] = 15

        所以最长上升子序列的长度为7
        注意:dp数组得到的不一定是真正的LIS
        比如:tr[] = 1 4 7 2 5 9 10 3
        得到的是1 2 3 9 10,而真正的LIS是1 2 5 9 10
        因此dp数组得到的不一定是真正的LIS,他只表示最长子序列长度的排好序的最小序列,对于最后一半将5换成3的意义是记录最小序列,便于后续数据的处理
         */
        int n = a.length;
        if (n <= 1) return n;

        int[] dp = new int[n + 1];
        int idx = 1;
        dp[idx] = a[0];
        // 利用贪心 + 二分查找进行更新
        for (int i = 1; i < n ; i++) {
            if (dp[idx] < a[i]) {
                idx++;
                dp[idx] = a[i];
            } else {
                int l = 1;
                int r = idx;
                int pos = 0;

                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (dp[mid] < a[i]) {
                        pos = mid;
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                dp[pos + 1] = a[i];
            }
        }
        return idx;
    }
}

Go代码

package main

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 该数组最长严格上升子序列的长度
 * @param a int整型一维数组 给定的数组
 * @return int整型
 */
func LIS(a []int) int {
	//https://blog.csdn.net/weixin_51216553/article/details/114678534
	/*
	   贪心+二分

	   所谓贪心,就是往死里贪,所以对于最大上升子序列,结尾元素越小,越有利于后面接上其他的数,也就可能变得更长
	   所以贪心的做法是,建立一个dp数组,dp[i[表示长度为i的LIS结尾元素的最小值,因此我们只需要维护dp数组即可

	   对于每一个数组的数,我们对他们进行判断,如果他大于等于dp[ans]的值,就把他放在数组后面,dp[++ans] = tr[i],
	   否则,就在dp中去找大一个大于等于他的位置pos,dp[pos] = tr[i]。如果从头扫一遍数组,时间复杂度还是O(n^2),
	   这与曹贼何异?!通过观察我们知道,这次维护的dp数组是单调递增的,所以就可以使出秘技二分之lower_bound来找pos的位置
	   我们举个栗子:

	       tr[] = 2 5 18 3 4 7 10 9 11 8 15
	       dp[1] = 2;
	       5大于2,所以dp[2] = 5
	       18大于5,所以dp[3] = 18
	       3小于18,所以二分去找,pos是2,所以dp[2] = 3
	       4小于18,所以二分去找,pos是3,所以dp[3] = 4
	       7大于4,所以dp[4] = 7
	       10大于7,所以dp[5] = 10
	       9小于10,所以二分去找,pos是5,dp[5] = 9
	       11大于9,所以dp[6] = 11
	       8小于11,所以二分去找,pos是5,dp[5] = 8
	       15大于11,所以dp[7] = 15

	   所以最长上升子序列的长度为7
	   注意:dp数组得到的不一定是真正的LIS
	   比如:tr[] = 1 4 7 2 5 9 10 3
	   得到的是1 2 3 9 10,而真正的LIS是1 2 5 9 10
	   因此dp数组得到的不一定是真正的LIS,他只表示最长子序列长度的排好序的最小序列,对于最后一半将5换成3的意义是记录最小序列,便于后续数据的处理
	*/
	n := len(a)
	if n <= 1 {
		return n
	}

	dp := make([]int, n+1)
	idx := 1
	dp[idx] = a[0]

	//利用贪心+二分查找进行更新
	for i := 1; i < n; i++ {
		if dp[idx] < a[i] {
			idx++
			dp[idx] = a[i]
		} else {
			l := 1
			r := idx
			pos := 0
			for l <= r {
				mid := (l + r) >> 1
				if dp[mid] < a[i] {
					pos = mid
					l = mid + 1
				} else {
					r = mid - 1
				}
			}
			dp[pos+1] = a[i]
		}
	}

	return idx
}

PHP代码

<?php


/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 该数组最长严格上升子序列的长度
 * @param a int整型一维数组 给定的数组
 * @return int整型
 */
function LIS( $a )
{
      //https://blog.csdn.net/weixin_51216553/article/details/114678534
    /*
       贪心+二分

       所谓贪心,就是往死里贪,所以对于最大上升子序列,结尾元素越小,越有利于后面接上其他的数,也就可能变得更长
       所以贪心的做法是,建立一个dp数组,dp[i[表示长度为i的LIS结尾元素的最小值,因此我们只需要维护dp数组即可

       对于每一个数组的数,我们对他们进行判断,如果他大于等于dp[ans]的值,就把他放在数组后面,dp[++ans] = tr[i],
       否则,就在dp中去找大一个大于等于他的位置pos,dp[pos] = tr[i]。如果从头扫一遍数组,时间复杂度还是O(n^2),
       这与曹贼何异?!通过观察我们知道,这次维护的dp数组是单调递增的,所以就可以使出秘技二分之lower_bound来找pos的位置
       我们举个栗子:

           tr[] = 2 5 18 3 4 7 10 9 11 8 15
           dp[1] = 2;
           5大于2,所以dp[2] = 5
           18大于5,所以dp[3] = 18
           3小于18,所以二分去找,pos是2,所以dp[2] = 3
           4小于18,所以二分去找,pos是3,所以dp[3] = 4
           7大于4,所以dp[4] = 7
           10大于7,所以dp[5] = 10
           9小于10,所以二分去找,pos是5,dp[5] = 9
           11大于9,所以dp[6] = 11
           8小于11,所以二分去找,pos是5,dp[5] = 8
           15大于11,所以dp[7] = 15

       所以最长上升子序列的长度为7
       注意:dp数组得到的不一定是真正的LIS
       比如:tr[] = 1 4 7 2 5 9 10 3
       得到的是1 2 3 9 10,而真正的LIS是1 2 5 9 10
       因此dp数组得到的不一定是真正的LIS,他只表示最长子序列长度的排好序的最小序列,对于最后一半将5换成3的意义是记录最小序列,便于后续数据的处理
    */
     $n = count($a);
    if($n<=1){
        return $n;
    }

    $dp =[0];
    $idx = 1;
    $dp[$idx] = $a[0];

    // 利用贪心 + 二分查找进行更新
    for($i=1;$i<$n;$i++){
        if($dp[$idx] <$a[$i]){
            $idx++;
            $dp[$idx] = $a[$i];
        }else{
            $l=1;
            $r =$idx;
            $pos=0;
            while ($l<=$r){
                $mid = ($l+$r) >>1;
                if($dp[$mid] <$a[$i]){
                    $pos = $mid;
                    $l=$mid+1;
                }else{
                    $r = $mid-1;
                }
            }

            $dp[$pos+1] = $a[$i];
        }
    }
    return $idx;
}

C++代码

class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 该数组最长严格上升子序列的长度
     * @param a int整型vector 给定的数组
     * @return int整型
     */
    int LIS(vector<int>& a) {
        //https://blog.csdn.net/weixin_51216553/article/details/114678534
        /*
        贪心+二分

        所谓贪心,就是往死里贪,所以对于最大上升子序列,结尾元素越小,越有利于后面接上其他的数,也就可能变得更长
        所以贪心的做法是,建立一个dp数组,dp[i[表示长度为i的LIS结尾元素的最小值,因此我们只需要维护dp数组即可

        对于每一个数组的数,我们对他们进行判断,如果他大于等于dp[ans]的值,就把他放在数组后面,dp[++ans] = tr[i],
        否则,就在dp中去找大一个大于等于他的位置pos,dp[pos] = tr[i]。如果从头扫一遍数组,时间复杂度还是O(n^2),
        这与曹贼何异?!通过观察我们知道,这次维护的dp数组是单调递增的,所以就可以使出秘技二分之lower_bound来找pos的位置
        我们举个栗子:

            tr[] = 2 5 18 3 4 7 10 9 11 8 15
            dp[1] = 2;
            5大于2,所以dp[2] = 5
            18大于5,所以dp[3] = 18
            3小于18,所以二分去找,pos是2,所以dp[2] = 3
            4小于18,所以二分去找,pos是3,所以dp[3] = 4
            7大于4,所以dp[4] = 7
            10大于7,所以dp[5] = 10
            9小于10,所以二分去找,pos是5,dp[5] = 9
            11大于9,所以dp[6] = 11
            8小于11,所以二分去找,pos是5,dp[5] = 8
            15大于11,所以dp[7] = 15

        所以最长上升子序列的长度为7
        注意:dp数组得到的不一定是真正的LIS
        比如:tr[] = 1 4 7 2 5 9 10 3
        得到的是1 2 3 9 10,而真正的LIS是1 2 5 9 10
        因此dp数组得到的不一定是真正的LIS,他只表示最长子序列长度的排好序的最小序列,对于最后一半将5换成3的意义是记录最小序列,便于后续数据的处理
         */
        int n = a.size();
        if (n <= 1) {
            return n;
        }

        vector<int> dp(n + 1, 0);
        int idx = 1;
        dp[idx] = a[0];
        // 利用贪心 + 二分查找进行更新
        for (int i = 1; i < n; i++) {
            if (dp[idx] < a[i]) {
                dp[++idx] = a[i];
            } else {
                int l = 1;
                int r = idx;
                int pos = 0;
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (dp[mid] < a[i]) {
                        pos = mid;
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                dp[pos + 1] = a[i];
            }
        }
        return idx;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这里一定有你不知道的VS调试技巧

目录 使用环境&#xff1a;Visual Studio 2022,如无特殊说明&#xff0c;都是在Debug、x64环境下编译 一.什么是BUG 二.调试快捷键 F9&#xff1a;创建断电或取消断点 条件断点&#xff1a;满足这个条件才触发 F5&#xff1a;启动调试&#xff0c;经常⽤来直接跳到下⼀个断…

Windows通过cmd运行快速启动应用

Windows如何通过cmd运行快速启动应用&#xff1f; 在Windows操作系统中&#xff0c;可以通过配置环境变量的方式将文件的路径配置到环境变量的path中&#xff0c;配置完成后可以在cmd中输入对应的应用名称即可启动应用&#xff0c;具体操作如下&#xff1a; 1. 添加应用程序路径…

【机器学习300问】102、什么是混淆矩阵?

一、混淆矩阵的定义 混淆矩阵是一种用于评估分类模型性能的评估指标。当模型对数据进行预测并将数据分配到预定义的类别时&#xff0c;混淆矩阵提供了一种直观的方式来总结这些预测与数据实际类别之间的对应关系。具体来说&#xff0c;它是一个表格。 二、分类模型性能评估一级…

项目启动 | 宏昌电器牵手盘古信息,数字化制造引领企业高质量发展

随着时代的发展&#xff0c;数字化转型已成为实现企业持续增长和塑造竞争优势不可或缺的关键因素。浙江宏昌电器科技股份有限公司&#xff08;以下简称为“宏昌电器”&#xff09;围绕企业战略发展需求&#xff0c;积极加速数字化转型升级进程&#xff0c;以数字化力量推动公司…

VS Code 开发小技巧

VS Code的开发小技巧 添加代码片段 平时开发的时候&#xff0c;可以快速创建一个空白的模板。 一个快速生成代码片段的网站&#xff1a;https://snippet-generator.app/ 打开网站&#xff0c;把常用的模板代码复制进去&#xff0c;就会自动生成VS Code可以使用的代码片段了。…

【上海大学计算机组成原理实验报告】六、内存系统实验

一、实验目的 学习内存访问机制。理解代码和数据的分区存放原理和技术。 二、实验原理 根据实验指导书的相关内容&#xff0c;地址寄存器MAR用来存放要进行读或写的存储器EM的地址。其内容经数据总线DBUS写入&#xff0c;因此必须在数据总线上具有数据后&#xff0c;配合MAR允…

element-ui表格全选

项目场景&#xff1a; 根据项目需求&#xff0c;要求在表格外加【全选】复选框&#xff0c;切换分页也需将每一行都勾选上 实现方式&#xff1a; 借用element-ui文档的这几个方法和属性 <el-checkboxv-model"checkAll"change"handleCheckAllChange"&g…

【linux】宝塔,首页挂载磁盘,显示使用情况

挂载前&#xff1a; 挂载后&#xff1a; 数据无价&#xff0c;建议&#xff1a;备份需要挂载的磁盘&#xff0c;或者使用新磁盘来进行操作。 1、下载自动挂载磁盘的脚本&#xff1a; wget -O auto_disk.sh http://download.bt.cn/tools/auto_disk.sh 2、给脚本添加执行权限&a…

深入剖析Java线程池的核心概念与源码解析:从Executors、Executor、execute逐一揭秘

文章目录 文章导图前言Executors、Executor、execute对比剖析Executors生成的线程池&#xff1f;线程池中的 execute 方法execute 方法的作用execute的工作原理拒绝策略 源码分析工作原理基本知识线程的状态线程池的状态线程池状态和线程状态总结线程池的状态信息和线程数量信息…

LeetCode题练习与总结:路径总和Ⅱ--113

一、题目描述 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], target…

基于Springboot驾校预约平台小程序的设计与实现(源码+数据库+文档)

一.项目介绍 系统角色&#xff1a;管理员、教练、学员 小程序(仅限于学员注册、登录)&#xff1a; 查看管理员发布的公告信息 查看管理员发布的驾校信息 查看所有教练信息、预约(需教练审核)、评论、收藏喜欢的教练 查看管理员发布的考试信息、预约考试(需管理…

算法题解记录27+++随机链表的复制(百日筑基)

一、题目描述&#xff1a; 题目难度&#xff1a;中等 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每…

CDH6.3.2安装文档

前置环境&#xff1a; 操作系统&#xff1a; CentOS Linux release 7.7 java JDK &#xff1a; 1.8.0_231 1、准备工作 准备以下安装包&#xff1a; Cloudera Manager: cloudera-manager-agent-6.3.1-1466458.el7.x86_64.rpm cloudera-manager-daemons-6.3.1-1466458.el…

linux安装MYSQL后,利用grep查看MYSQL初始密码

问题描述 linux安装mysql获取初始密码 解决方案&#xff1a; 通过查看日志获取初始密码 grep "password" /var/log/mysqld.loggrep 是一个用于在文本中查找特定字符串的工具。 /var/log/mysqld.log 是要搜索的文件路径&#xff0c;"password" 是要查找的…

树莓集团:构筑全国数字影像生态链

在数字化浪潮席卷全球的今天&#xff0c;数字影像技术正以前所未有的速度改变着我们的生活。成都树莓集团以远见卓识和坚定步伐&#xff0c;专注于全国数字影像生态链的建设&#xff0c;不断推动着文创产业的创新与发展。 树莓集团致力于打造一个完整的数字影像生态链&#xff…

CLIP--Learning Transferable Visual Models From Natural Language Supervision

参考&#xff1a;CLIP论文笔记--《Learning Transferable Visual Models From Natural Language Supervision》_visual n-grams模型-CSDN博客 openAI&#xff0c;2021&#xff0c;将图片和文字联系在一起&#xff0c;----->得到一个能非常好表达图片和文字的模型主题&#…

Java后端代码框架包设计-什么是Domain,BO,VO?我们改如何区分和定义?

我们先来看看一个项目的代码结构,如下图: 1.定义包名用domain这个单词是什么含义 在Java中,domain 这个单词通常用于表示应用程序的“领域模型”(Domain Model)或“领域层”(Domain Layer)。领域模型是描述系统业务逻辑和规则的对象集合,它通常包含实体(Entities)、…

构建一个文字冒险游戏:Python 编程实战

在本文中&#xff0c;我们将探索如何使用 Python 创建一个简单的文字冒险游戏。通过这个项目&#xff0c;你将了解到基础的编程技术&#xff0c;包括条件语句、函数和基本的用户输入处理&#xff0c;同时也能体会到文本游戏的魅力和设计的挑战。 项目概述 文字冒险游戏是一种…

Transformer中的位置编码PE(position encoding)

Transformer中的位置编码PE(position encoding) 1.提出背景 transformer模型的attention机制并没有包含位置信息&#xff0c;即一句话中词语在不同的位置时在transformer中是没有区别的 2.解决背景 给encoder层和decoder层的输入添加了一个额外的向量Positional Encoding&a…

linux进程的加载和启动过程分析

我们的源代码通过预处理,编译,汇编,链接后形成可执行文件,那么当我们在终端敲下指令$ ./a.out argv1 argv2 后,操作系统是怎么将我们的可执行文件加载并运行的呢? 首先知道,计算机的操作系统的启动程序是写死在硬件上的,每次计算机上电时,都将自动加载启动程序,之后…