class072 最长递增子序列问题与扩展【算法】

news2024/12/22 18:57:07

class072 最长递增子序列问题与扩展【算法】

在这里插入图片描述
在这里插入图片描述

code1 300. 最长递增子序列

// 最长递增子序列和最长不下降子序列
// 给定一个整数数组nums
// 找到其中最长严格递增子序列长度、最长不下降子序列长度
// 测试链接 : https://leetcode.cn/problems/longest-increasing-subsequence/

dp[i]:以i位置作结尾的最长递增子序列长度
返回Max(dp[…])

优化
ends[i]:目前所有长度为i+1的递增子序列的最小结尾
返回len

code1 动态规划
code2 优化

package class072;

// 最长递增子序列和最长不下降子序列
// 给定一个整数数组nums
// 找到其中最长严格递增子序列长度、最长不下降子序列长度
// 测试链接 : https://leetcode.cn/problems/longest-increasing-subsequence/
public class Code01_LongestIncreasingSubsequence {

	// 普通解法的动态规划
	// 时间复杂度O(n^2),数组稍大就会超时
	public static int lengthOfLIS1(int[] nums) {
		int n = nums.length;
		int[] dp = new int[n];
		int ans = 0;
		for (int i = 0; i < n; i++) {
			dp[i] = 1;
			for (int j = 0; j < i; j++) {
				if (nums[j] < nums[i]) {
					dp[i] = Math.max(dp[i], dp[j] + 1);
				}
			}
			ans = Math.max(ans, dp[i]);
		}
		return ans;
	}

	// 最优解
	// 时间复杂度O(n * logn)
	public static int lengthOfLIS2(int[] nums) {
		int n = nums.length;
		int[] ends = new int[n];
		// len表示ends数组目前的有效区长度
		// ends[0...len-1]是有效区,有效区内的数字一定严格升序
		int len = 0;
		for (int i = 0, find; i < n; i++) {
			find = bs1(ends, len, nums[i]);
			if (find == -1) {
				ends[len++] = nums[i];
			} else {
				ends[find] = nums[i];
			}
		}
		return len;
	}

	// "最长递增子序列"使用如下二分搜索 :
	// ends[0...len-1]是严格升序的,找到>=num的最左位置
	// 如果不存在返回-1
	public static int bs1(int[] ends, int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] >= num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

	// 如果求最长不下降子序列,那么使用如下的二分搜索 :
	// ends[0...len-1]是不降序的
	// 在其中找到>num的最左位置,如果不存在返回-1
	// 如果求最长不下降子序列,就在lengthOfLIS中把bs1方法换成bs2方法
	// 已经用对数器验证了,是正确的
	public static int bs2(int[] ends, int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] > num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}

code2 354. 俄罗斯套娃信封问题

// 俄罗斯套娃信封问题
// 给你一个二维整数数组envelopes ,其中envelopes[i]=[wi, hi]
// 表示第 i 个信封的宽度和高度
// 当另一个信封的宽度和高度都比这个信封大的时候
// 这个信封就可以放进另一个信封里,如同俄罗斯套娃一样
// 请计算 最多能有多少个信封能组成一组“俄罗斯套娃”信封
// 即可以把一个信封放到另一个信封里面,注意不允许旋转信封
// 测试链接 : https://leetcode.cn/problems/russian-doll-envelopes/

排序策略:宽度从小到大;宽度一样,高度从大到小
构成高度数组,求最长递增子序列的长度

package class072;

import java.util.Arrays;

// 俄罗斯套娃信封问题
// 给你一个二维整数数组envelopes ,其中envelopes[i]=[wi, hi]
// 表示第 i 个信封的宽度和高度
// 当另一个信封的宽度和高度都比这个信封大的时候
// 这个信封就可以放进另一个信封里,如同俄罗斯套娃一样
// 请计算 最多能有多少个信封能组成一组“俄罗斯套娃”信封
// 即可以把一个信封放到另一个信封里面,注意不允许旋转信封
// 测试链接 : https://leetcode.cn/problems/russian-doll-envelopes/
public class Code02_RussianDollEnvelopes {

	public static int maxEnvelopes(int[][] envelopes) {
		int n = envelopes.length;
		// 排序策略:
		// 宽度从小到大
		// 宽度一样,高度从大到小
		Arrays.sort(envelopes, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (b[1] - a[1]));
		int[] ends = new int[n];
		int len = 0;
		for (int i = 0, find, num; i < n; i++) {
			num = envelopes[i][1];
			find = bs(ends, len, num);
			if (find == -1) {
				ends[len++] = num;
			} else {
				ends[find] = num;
			}
		}
		return len;
	}

	public static int bs(int[] ends, int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] >= num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}

code3 2111. 使数组 K 递增的最少操作次数

// 使数组K递增的最少操作次数
// 给你一个下标从0开始包含n个正整数的数组arr,和一个正整数k
// 如果对于每个满足 k <= i <= n-1 的下标 i
// 都有 arr[i-k] <= arr[i] ,那么称 arr 是K递增的
// 每一次操作中,你可以选择一个下标i并将arr[i]改成任意正整数
// 请你返回对于给定的 k ,使数组变成K递增的最少操作次数
// 测试链接 : https://leetcode.cn/problems/minimum-operations-to-make-the-array-k-increasing/

把每一组分出来
求出每一组的最长不下降子序列的长度,
修改长度就是总长度减去最长不下降子序列的长度
每一组的修改长度求和即为答案

package class072;

// 使数组K递增的最少操作次数
// 给你一个下标从0开始包含n个正整数的数组arr,和一个正整数k
// 如果对于每个满足 k <= i <= n-1 的下标 i
// 都有 arr[i-k] <= arr[i] ,那么称 arr 是K递增的
// 每一次操作中,你可以选择一个下标i并将arr[i]改成任意正整数
// 请你返回对于给定的 k ,使数组变成K递增的最少操作次数
// 测试链接 : https://leetcode.cn/problems/minimum-operations-to-make-the-array-k-increasing/
public class Code03_MinimumOperationsToMakeArraykIncreasing {

	public static int MAXN = 100001;

	public static int[] nums = new int[MAXN];

	public static int[] ends = new int[MAXN];

	public static int kIncreasing(int[] arr, int k) {
		int n = arr.length;
		int ans = 0;
		for (int i = 0, size; i < k; i++) {
			size = 0;
			// 把每一组的数字放入容器
			for (int j = i; j < n; j += k) {
				nums[size++] = arr[j];
			}
			// 当前组长度 - 当前组最长不下降子序列长度 = 当前组至少需要修改的数字个数
			ans += size - lengthOfNoDecreasing(size);
		}
		return ans;
	}

	// nums[0...size-1]中的最长不下降子序列长度
	public static int lengthOfNoDecreasing(int size) {
		int len = 0;
		for (int i = 0, find; i < size; i++) {
			find = bs(len, nums[i]);
			if (find == -1) {
				ends[len++] = nums[i];
			} else {
				ends[find] = nums[i];
			}
		}
		return len;
	}

	public static int bs(int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (num < ends[m]) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}

code4 646. 最长数对链

// 最长数对链
// 给你一个由n个数对组成的数对数组pairs
// 其中 pairs[i] = [lefti, righti] 且 lefti < righti
// 现在,我们定义一种 跟随 关系,当且仅当 b < c 时
// 数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后面
// 我们用这种形式来构造 数对链
// 找出并返回能够形成的最长数对链的长度
// 测试链接 : https://leetcode.cn/problems/maximum-length-of-pair-chain/

按开头有序
ends数组,放数对的最小结尾,查要查数对的开头,第一个比它大的,再更新最小结尾。

package class072;

import java.util.Arrays;

// 最长数对链
// 给你一个由n个数对组成的数对数组pairs
// 其中 pairs[i] = [lefti, righti] 且 lefti < righti
// 现在,我们定义一种 跟随 关系,当且仅当 b < c 时
// 数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后面
// 我们用这种形式来构造 数对链
// 找出并返回能够形成的最长数对链的长度
// 测试链接 : https://leetcode.cn/problems/maximum-length-of-pair-chain/
public class Code04_MaximumLengthOfPairChain {

	public static int findLongestChain(int[][] pairs) {
		int n = pairs.length;
		// 数对根据开始位置排序,从小到大
		// 结束位置无所谓!
		Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
		// 结尾的数值
		int[] ends = new int[n];
		int len = 0;
		for (int[] pair : pairs) {
			int find = bs(ends, len, pair[0]);
			if (find == -1) {
				ends[len++] = pair[1];
			} else {
				ends[find] = Math.min(ends[find], pair[1]);
			}
		}
		return len;
	}

	// >= num最左位置
	public static int bs(int[] ends, int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] >= num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}

code5 P8776 [蓝桥杯 2022 省 A] 最长不下降子序列

// 有一次修改机会的最长不下降子序列
// 给定一个长度为n的数组arr,和一个整数k
// 只有一次机会可以将其中连续的k个数全修改成任意一个值
// 这次机会你可以用也可以不用,请返回最长不下降子序列长度
// 1 <= k, n <= 10^5
// 1 <= arr[i] <= 10^6
// 测试链接 : https://www.luogu.com.cn/problem/P8776
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"Main",可以直接通过

    		   [k个z]  	z
0   		i       	j
最后小于z的 	+k 		+包含j位置的
不下降子序列的长度     不下降子序列的长度

包含j位置的不下降子序列的长度等同于求出n-1…j的最长不上升子序列
ends存放最大结尾

673. 最长递增子序列的个数
给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。
测试链接:https://leetcode.cn/problems/number-of-longest-increasing-subsequence/

重剑无锋,大巧不工

package class072;

// 有一次修改机会的最长不下降子序列
// 给定一个长度为n的数组arr,和一个整数k
// 只有一次机会可以将其中连续的k个数全修改成任意一个值
// 这次机会你可以用也可以不用,请返回最长不下降子序列长度
// 1 <= k, n <= 10^5
// 1 <= arr[i] <= 10^6
// 测试链接 : https://www.luogu.com.cn/problem/P8776
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"Main",可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Code05_LongestNoDecreaseModifyKSubarray {

	public static int MAXN = 100001;

	public static int[] arr = new int[MAXN];

	public static int[] right = new int[MAXN];

	public static int[] ends = new int[MAXN];

	public static int n, k;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken();
			k = (int) (in.nval);
			for (int i = 0; i < n; i++) {
				in.nextToken();
				arr[i] = (int) in.nval;
			}
			if (k >= n) {
				out.println(n);
			} else {
				out.println(compute());
			}
		}
		out.flush();
		out.close();
		br.close();
	}

	public static int compute() {
		right();
		int len = 0;
		int ans = 0;
		for (int i = 0, j = k, find, left; j < n; i++, j++) {
			find = bs2(len, arr[j]);
			left = find == -1 ? len : find;
			ans = Math.max(ans, left + k + right[j]);
			find = bs2(len, arr[i]);
			if (find == -1) {
				ends[len++] = arr[i];
			} else {
				ends[find] = arr[i];
			}
		}
		ans = Math.max(ans, len + k);
		return ans;
	}

	// 生成辅助数组right
	// right[j] :
	// 一定以arr[j]做开头的情况下,arr[j...]上最长不下降子序列长度是多少
	// 关键逻辑 :
	// 一定以arr[i]做开头的情况下,arr[i...]上最长不下降子序列
	// 就是!从n-1出发来看(从右往左遍历),以arr[i]做结尾的情况下的最长不上升子序列
	public static void right() {
		int len = 0;
		for (int i = n - 1, find; i >= 0; i--) {
			find = bs1(len, arr[i]);
			if (find == -1) {
				ends[len++] = arr[i];
				right[i] = len;
			} else {
				ends[find] = arr[i];
				right[i] = find + 1;
			}
		}
	}

	// 求最长不上升子序列长度的二分
	// ends[0...len-1]是降序的,找到<num的最左位置
	// 不存在返回-1
	public static int bs1(int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] < num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

	// 求最长不下降子序列长度的二分
	// ends[0...len-1]是升序的,找到>num的最左位置
	// 不存在返回-1
	public static int bs2(int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] > num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}

2023-11-09 20:58:41

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

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

相关文章

Java 简易版 UDP 多人聊天室

服务端 import java.io.*; import java.net.*; import java.util.ArrayList; public class Server{public static ServerSocket server_socket;public static ArrayList<Socket> socketListnew ArrayList<Socket>(); public static void main(String []args){try{…

分集水器在中央空调系统中的应用,工作原理压力选型参数尺寸

​ 1&#xff1a;中央空调分集水器设备介绍 分集水器是由分水器和集水器组合而成的水流量分配和会集装置。分水器是将一路进水通过一个容器分为几路输出的设备&#xff0c;而集水器则是将多路进水通过一个容器一路输出的设备。其管理若干的支路管道&#xff0c;分别包括回水支路…

1-1、Java概述

语雀原文链接 文章目录 1、Java发展2、Java体系结构3、Java特点 1、Java发展 1990年&#xff0c;Sun公司(Stanford University Network,斯坦福大学网络公司)詹姆斯高斯林推出的一门语言最开始注册的名字oak语言(橡树)&#xff0c;重名了被迫改成Java2009年Sun公司被甲骨文Ora…

IP地址定位技术为网络安全建设提供全新方案

随着互联网的普及和数字化进程的加速&#xff0c;网络安全问题日益引人关注。网络攻击、数据泄露、欺诈行为等安全威胁层出不穷&#xff0c;对个人隐私、企业机密和社会稳定构成严重威胁。在这样的背景下&#xff0c;IP地址定位技术应运而生&#xff0c;为网络安全建设提供了一…

向日葵远程控制鼠标异常的问题

​ 在通过向日葵进行远程控制的时候&#xff0c;可能会遇到鼠标位置异常的问题。此时&#xff0c;不管怎么移动鼠标&#xff0c;都会停留在屏幕最上方&#xff0c;而无法点击到正确的位置。如图&#xff1a; 此时&#xff0c;如果启用了“被控端鼠标”功能&#xff0c;可以正…

代码随想录二刷 |二叉树 |二叉树的最大深度

代码随想录二刷 &#xff5c;二叉树 &#xff5c;二叉树的最大深度 题目描述解题思路代码实现 题目描述 104.二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a;…

键盘打字盲打练习系列之矫正坐姿——4

一.欢迎来到我的酒馆 盲打&#xff0c;矫正坐姿&#xff01; 目录 一.欢迎来到我的酒馆二.继续练习二.矫正坐姿1.键盘鼠标选购指南2.椅子选购指南 三.改善坐姿建议 二.继续练习 前面的章节&#xff0c;我们重点向大家介绍了主键盘区指法和键盘键位。经过一个系列的教程学习&am…

table中cellspacing、cellpadding的css替代写法

table中cellspacing、cellpadding的css替代写法 table{border:0;margin:0;border-collapse:collapse;border-spacing:0;} /*控制cellspacing*/ table td{padding:0;} /*控制cellpadding*/

用chatGPT开发项目:我想的无人的智慧树网站 流量之神 利用人工智能的算法将人吸引住 GPT4是不是越来越难用了,问一下就要证明一下自己是不是人类

广度发散&#xff1a;让AI给出时代或今日或你关注的热点事件 比如采集新闻头条&#xff0c;根据内容或标题&#xff0c;以不同的角度&#xff0c;或各种人群的角色&#xff0c;生成50篇简短的文章。一下就能占传统的搜索引擎。这是AI最擅长的【千人千面&#xff0c;海量生成】…

C语言中实参和形参的值传递,一直都不理解

C语言中实参和形参的值传递&#xff0c;一直都不理解 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「C语言从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01…

Unity 实现单例模式

目录 基本概念 饿汉模式(推荐) 懒汉模式&#xff1a; 基本概念 单例模式&#xff1a;类只有一个实例&#xff0c;一般使用static来实现单例模式&#xff1b; 比如&#xff1a;有一个Test类,实现了单例&#xff0c;假设这个唯一的实例名为SingTonle,实例在类内被实现并被stat…

Flask和Vue框架实现WebSocket消息通信

1 安装环境 1.1 安装Flask环境 主要的安装包 Flask、Flask-SocketIO&#xff0c;注意Python版本要求3.6 # Flask-SocketIO参考地址 https://flask-socketio.readthedocs.io/en/latest/ https://github.com/miguelgrinberg/flask-socketio更新基础环境 # 更新pip python -m …

软考高级备考-系统架构师(机考后新版教材的备考过程与资料分享)

软考高级-系统架构设计师 考试复盘1.考试结果2.备考计划3.个人心得 资料分享 考试复盘 1.考试结果 三科压线过&#xff0c;真是太太太太太太太幸运了。上天对我如此眷顾&#xff0c;那不得不分享下我的备考过程以及一些备考资料&#xff0c;帮助更多小伙伴通过考试。 2.备考…

HarmonyOS4.0从零开始的开发教程10Video组件的使用

HarmonyOS&#xff08;九&#xff09;Video组件的使用 概述 在手机、平板或是智慧屏这些终端设备上&#xff0c;媒体功能可以算作是我们最常用的场景之一。无论是实现音频的播放、录制、采集&#xff0c;还是视频的播放、切换、循环&#xff0c;亦或是相机的预览、拍照等功能…

jQuery中点击按钮发送多次请求

jQuery中点击按钮发送多次请求 /* 采用以下常规触发按钮去执行回调函数&#xff0c;可能会发送多次请求。并且会影响到数据库表 原因分析&#xff1a;可能是前端原型&#xff0c;绑定了多次事件。 */ $("#saveBtn").click(function (){$.ajax({}) }/* 解决办法如下&a…

windows下分卷解压文件

我的文件是这样的&#xff1a; 存放路径为&#xff1a;C:\Users\Luli_study\MICCAI_MMAC\fudanuniversity\DDR dataset 首先要进入分卷文件的目录cd&#xff1a; 第一步&#xff1a;cd /path/o/分卷问文件目录 第二步&#xff1a; 执行之后的结果(红色框出来的)&#xff1a; …

基于Spring+Spring boot的SpringBoot在线电子商城管理系统

SSM毕设分享 基于SpringSpring boot的SpringBoot在线电子商城管理系统 1 项目简介 Hi&#xff0c;各位同学好&#xff0c;这里是郑师兄&#xff01; 今天向大家分享一个毕业设计项目作品【基于SpringSpring boot的SpringBoot在线电子商城管理系统】 师兄根据实现的难度和等级…

装修干货,卫生间干湿分离的5个建议。福州中宅装饰,福州装修

你是否也曾为卫生间的干湿分离烦恼&#xff1f;小编今天就给大家带来5点建议&#xff0c;让你轻松解决这个问题&#xff01; ①使用玻璃淋浴房 使用玻璃淋浴房是一种常见的干湿分离方法。玻璃淋浴房可以防水、防滑&#xff0c;而且清洁起来也比较方便。 ②使用淋浴屏风 淋浴屏…

【展望2024】,从软件测试用例开始学习起

1. 测试用例的概念 测试用例就是测试人员向被测试系统发起的一组集合&#xff0c;该集合包括测试环境&#xff0c;测试数据&#xff0c;测试步骤&#xff0c;预期结果 2. 设计测试用例的好处 在测试前都要先设计测试用例&#xff0c;设计测试用例有如下好处&#xff1a; 测…

Java数据结构《二叉排序树的插入删除和查找》(难度系数100)

一、前言&#xff1a; 这是怀化学院的&#xff1a;Java数据结构中的一道难度偏难(偏难理解)的一道编程题(此方法为博主自己研究与学习一名叫qing影的博主&#xff0c;问题基本解决&#xff0c;若有bug欢迎下方评论提出意见&#xff0c;我会第一时间改进代码&#xff0c;谢谢&am…