题目描述与背景介绍

背景题目:

[674. 最长连续递增序列]https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/

[300. 最长递增子序列]https://leetcode-cn.com/problems/longest-increasing-subsequence/

这两个都是DP的经典题目,674比较简单。

代码:

class Solution {
public int findLengthOfLCIS(int[] nums) {
if(nums.length == 0){//边界判断
return 0;
}else if(nums.length == 1){
return 1;
}
int[] dp = new int[nums.length];
dp[0] = 1;//dp[i]是以nums[i]结尾的最长连续递增子序列的长度
int maxLen = 1;
for(int i = 1;i < nums.length;i++){
if(nums[i] > nums[i - 1]){
dp[i] = dp[i - 1] + 1;
}else{
dp[i] = 1;
}
maxLen = Math.max(dp[i],maxLen);
}
return maxLen;
}
}

300题相对来说暴力解法也相对简单。

求解dp[i] 时,要遍历 dp[0] ~ dp[i-1],寻找满足nums[j] < nums[i] 的j(首先j<i),那么dp[i] = dp[j] + 1,若找不到这样的j,则dp[i] = 1,因为每个数字单独都能成为一个子序列。

代码:

class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length == 1){
return 1;
}
int[] dp = new int[nums.length];
dp[0] = 1;//初始化
int maxLen = 1;
for(int i = 1;i < nums.length;i++){
dp[i] = 1;//每个数字首先单独成为一个序列
for(int j = 0;j < i;j++){
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i], dp[j] + 1);//更新dp[i]
}
}
maxLen = Math.max(maxLen, dp[i]);//更新最大长度。
}
return maxLen;
}
}

300题的优化

仔细分析发现,在求解dp[i]时,要遍历nums[0]~nums[i - 1]。总的来说,上述暴力解法的时间复杂度是O(n^2) 。

O(n^2) 的复杂度还是有些太大,而 O(n^2) 下稍微优化有 O(nlogn) 的算法。

寻找满足条件的j时,其实可以利用二分查找。

用f[i]表示递增子序列长度为i的末尾最小元素。比如有2个不同的长度为3的递增子序列,一个末尾元素为9,一个末尾元素为12,那么,此时f[i]=12。

一旦这样规定,f[]就是单调递增的,长度为i(假设i>j)的子序列的末尾最小元素一定大于长度为j的子序列的末尾最小元素。

为什么?

取所有长度为i的子序列构成的集合为A和所有长度为j的子序列构成的集合为B(假设i>j)。

长度为i的子序列集合中一定存在包含某个长度为j的子序列,及存在a∈A,使得b∈B满足b是a的子序列。

f[i]是A中子序列末尾数的最小值,f[j]是B中子序列末尾数的最小值。则f[i]>=a(末尾)>b(末尾)>=f[j],因此f[]严格单调递增。

二分查找时要找到使得f[j]< nums[i] <= f[j+1] 成立的最大的j,返回j。

class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length == 1){
return 1;
}
int[] dp = new int[nums.length];
dp[0] = 1;//初始化
int[] f = new int[nums.length + 1];
f[1] = nums[0];
int maxLen = 1;
for(int i = 1;i < nums.length;i++){
dp[i] = binarySearch(f,1,maxLen,nums[i]) + 1;
if(f[dp[i]] == 0||nums[i] < f[dp[i]]){//更新f[],如果原来是0,直接更新,如果不是,比较大小。
f[dp[i]] = nums[i];
}
maxLen = Math.max(maxLen, dp[i]);//更新最大长度。
}
return maxLen;
}
private int binarySearch(int[] f,int first,int end,int curNumber){
if(end < first){
return 0;
}
if(curNumber <= f[first]){
return first - 1;
}
if(curNumber > f[end]){
return end;
}else if(curNumber == f[end]){
return end - 1;
}
if(curNumber > f[(first+end)/2]){
return binarySearch(f,(first+end)/2 + 1,end,curNumber);
}else{
return binarySearch(f,first,(first+end)/2 - 1,curNumber);
}
}
}

本题

[354. 俄罗斯套娃信封问题]https://leetcode-cn.com/problems/russian-doll-envelopes/

在本题中,要找到二维的最长递增子序列的长度,同时满足

Wi<Wj<...<Wk

Hi<Hj<...<Hk

但本题并不是一维递增子序列问题的简单扩展,原因是信封不按照数组中的先后顺序。

如何获取套娃时这些信封的先后顺序呢?

简单来说有以下三种方法:

  • 按某一维度升序排列
  • 按某一维度升序排列,出现相等时另一位维度在此基础上降序排列
  • 按面积升序排列

    按某一维度升序排列,出现相等时另一位维度在此基础上降序排列:

    第一维度相等时,无论第二维度如何都不能满足题意,因此,第二维降序可以保证任意子序列只选择第一维度相等的其中一个。

    暴力搜索代码:
class Solution {
public int maxEnvelopes(int[][] envelopes) { Arrays.sort(envelopes,new Comparator<int[]>(){
public int compare(int[] ob1,int[] ob2){
if(ob1[0]>ob2[0]){
return 1;
}else if(ob1[0]<ob2[0]){
return -1;
}else{
if(ob1[1]<ob2[1]){
return 1;
}else if(ob1[1]>ob2[1]){
return -1;
}else{
return 0;
}
}
}
}); int[] dp = new int[envelopes.length];
dp[0] = 1;
if(envelopes.length>1){
int maxEnvelopes = 1;
for(int i = 1;i<envelopes.length;i++){
dp[i] = 1;
for(int j = 0;j < i;j++){
if(envelopes[i][1] > envelopes[j][1]){
dp[i] = Math.max(dp[i],dp[j] + 1);
}
}
maxEnvelopes = Math.max(maxEnvelopes,dp[i]);
}
return maxEnvelopes;
}else{
return 1;
}
}
}

二分查找代码:

class Solution {
public int maxEnvelopes(int[][] envelopes) { Arrays.sort(envelopes,new Comparator<int[]>(){
public int compare(int[] ob1,int[] ob2){
if(ob1[0]>ob2[0]){
return 1;
}else if(ob1[0]<ob2[0]){
return -1;
}else{
if(ob1[1]<ob2[1]){
return 1;
}else if(ob1[1]>ob2[1]){
return -1;
}else{
return 0;
}
}
}
}); int[] dp = new int[envelopes.length];
dp[0] = 1;
int[] f = new int[envelopes.length + 1];
f[1] = envelopes[0][1];
if(envelopes.length>1){
int maxEnvelopes = 1;
for(int i = 1;i<envelopes.length;i++){
dp[i] = binarySearch(f,1,maxEnvelopes,envelopes[i][1]) + 1;
if(f[dp[i]]==0||envelopes[i][1]<f[dp[i]]){//更新f[]数组
f[dp[i]] = envelopes[i][1];
}
maxEnvelopes = Math.max(maxEnvelopes,dp[i]);
}
return maxEnvelopes;
}else{
return 1;
}
}
private int binarySearch(int[] f,int first,int end,int curNumber){
if(curNumber > f[end]){
return end;
}else if(curNumber == f[end]){
return end - 1;
}
if(curNumber <= f[first]){
return first - 1;
}
if(curNumber > f[(first + end)/2]){
return binarySearch(f,(first + end)/2+1,end,curNumber);
}else{
return binarySearch(f,first,(first + end)/2-1,curNumber);
}
}
}

排序时只考虑第一维度:

class Solution {
public int maxEnvelopes(int[][] envelopes) { Arrays.sort(envelopes,new Comparator<int[]>(){
public int compare(int[] ob1,int[] ob2){//排序时只考虑第一维度
if(ob1[0]>ob2[0]){
return 1;
}else if(ob1[0]<ob2[0]){
return -1;
}else{
return 0;
}
}
}); int[] dp = new int[envelopes.length];
dp[0] = 1;
if(envelopes.length>1){
int maxEnvelopes = 1;
for(int i = 1;i<envelopes.length;i++){
dp[i] = 1;
for(int j = 0;j < i;j++){
if(envelopes[i][1] > envelopes[j][1]&&envelopes[i][0] > envelopes[j][0]){//由于第一维度可能存在相等的数,加一个第一维度的比较
dp[i] = Math.max(dp[i],dp[j] + 1);
}
}
maxEnvelopes = Math.max(maxEnvelopes,dp[i]);
}
return maxEnvelopes;
}else{
return 1;
}
}
}

二维动态规划&&二分查找的动态规划&&最长递增子序列&&最长连续递增子序列的更多相关文章

  1. 递归分治算法之二维数组二分查找(Java版本)

    [java] /** * 递归分治算法学习之二维二分查找 * @author Sking 问题描述: 存在一个二维数组T[m][n],每一行元素从左到右递增, 每一列元素从上到下递增,现在需要查找元素 ...

  2. 【c语言】二维数组中的查找,杨氏矩阵在一个二维数组中,每行都依照从左到右的递增的顺序排序,输入这种一个数组和一个数,推断数组中是否包括这个数

    // 二维数组中的查找,杨氏矩阵在一个二维数组中.每行都依照从左到右的递增的顺序排序. // 每列都依照从上到下递增的顺序排序.请完毕一个函数,输入这种一个数组和一个数.推断数组中是否包括这个数 #i ...

  3. ACM_二维数组的查找

    二维数组的查找 Time Limit: 2000/1000ms (Java/Others) Problem Description: 给定一个n*m的二维数组,保证a[i][j] < a[i+1 ...

  4. 剑指Offer01之二维数组中查找目标数

    剑指Offer之二维数组中查找目标数 题目描述 ​ 在一个二维数组中(每个一维数组的长度相等),每一行都是从左到右递增的顺序排序,每一列都是从上到下递增的顺序排序,输入这样一个二维数组和一个整数,判断 ...

  5. 牛客网剑指offer 二维数组的查找

    题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 解题思路 该题有很多种 ...

  6. 剑指offer:2.二维数组的查找(Java版)

    备注:本文参照<剑指offer第二版> 题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数, 输入这样的一个二维数组和一个整数 ...

  7. [luogu4479][BJWC2018]第k大斜率【二维偏序+二分+离散化+树状数组】

    传送门 https://www.luogu.org/problemnew/show/P4479 题目描述 在平面直角坐标系上,有 n 个不同的点.任意两个不同的点确定了一条直线.请求出所有斜率存在的直 ...

  8. 剑指offer(1)二维数组的查找

    限制今天起开始也刷剑指offer啦,一步一步来. 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数, ...

  9. 二维数组的查找(JAVA)

    二维数组查找 解题思路:找到该二维数组的特殊点,易知该二维数组左下角的那个点很特殊.从这个点往右看,数值都在变大:而往上看,数值都在变小.所以 我们可以将这个点的索引设为起点(i,j),当比目标数大时 ...

随机推荐

  1. vue2路由之指定滑动位置scrollBehavior-(载转)

    看源码的时候看到这个属性: 新手自然不知道这个是什么东西了,查了下vue  API: https://router.vuejs.org/en/advanced/scroll-behavior.html ...

  2. jmeter中beanshell postprocessor结合fastjson库提取不确定个数的json参数

    在项目实践中,遇到了这样一个问题.用jmeter作http接口测试,需要的接口参数个数是不确定的.也就是说,在每次测试中,根据情况不同,可能页面中的列表中所含的参数个数是不确定的,那么要提取的参数个数 ...

  3. Maven 阿里云镜像配置

    1. 为什么要配置 Maven 阿里云镜像 安装 Maven 后默认是从国外 Maven 中央仓库下载内容,而下载速度简直可以用龟速来形容,不仅慢而且还经常出错,简直让人抓狂. 这时国内大厂阿里巴巴, ...

  4. 控制器网关/dns设置

    如果控制器ping内网可以,但是ping不同外网,十有八九是因为网关的问题,可以使用route命令设置网关,如设置为192.168.31.1(不是192.168.31.0),route add def ...

  5. java面试一日一题:再谈垃圾回收器中的串行、并行、并发

    问题:请讲下java中垃圾回收器的串行.并行.并发 分析:该问题主要考察在垃圾回收过程中垃圾回收线程和用户线程的关系 回答要点: 主要从以下几点去考虑, 1.串行.并行.并发的概念 2.如何考虑串行. ...

  6. 分布式深度学习DDL解析

    分布式深度学习DDL解析 一.概述 给一个庞大的GPU集群,在实际的应用中,现有的大数据调度器会导致长队列延迟和低的性能,该文章提出了Tiresias,即一个GPU集群的调度器,专门适应分布式深度学习 ...

  7. Wide-Bandgap宽禁带(WBG)器件(如GaN和SiC)市场将何去何从?

    Wide-Bandgap宽禁带(WBG)器件(如GaN和SiC)市场将何去何从? Where Is the Wide-Bandgap Market Going? 电力电子在采用宽禁带(WBG)器件(如 ...

  8. 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态

    嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程 ...

  9. python_request 使用jsonpath取值结果,进行接口关联

    一.jsonpath的安装 pip   install  jsonpath 二.使用举例 import jsonpath d1={"token":"hjshdsjhdsj ...

  10. 操作系统-gcc编译器驱动程序

    gcc编译器驱动程序,读取x.c文件,翻译成可执行目标文件x 1.预处理阶段 预处理器(cpp)将x.c(源程序,文本文件)中的#等直接插入程序文本中,成为另一个c程序x.i(文本文件) 2.编译阶段 ...