动态规划----最长递增子序列问题(LIS)
题目:
输出最长递增子序列的长度,如输入 4 2 3 1 5 6,输出 4 (因为 2 3 5 6组成了最长递增子序列)。
暴力破解法:这种方法很简单,两层for循环搞定,时间复杂度是O(N2)。
动态规划:之前我们使用动态规划去解决一般是创建一维数组或者二维数组来构建出dp表,利用之前的历史上dp表中的值进行相关的处理求解出这个过程中的几个最大值,最小值,然后相加减来得出dp表的当前元素的值,所以我们会想,先创建一个一维数组,因为数组中选择的元素的范围在进行变化,所以dp表表示的值为截取到当前范围内最长的递增子序列是多少,但是在填表的过程中发现这种一般方法是不行的。但是这作为一道经典的题目,思路本身是很难的,有人想出来了它的dp解法,我们可以通过借鉴它的思路来帮助我们扩展理解dp算法。
那么它的思路是:dp[i]表示的意思是必须包含以dp[i]结尾的最长递增子序列,那么它的值就是以dp[i]结尾的最长递增子序列长度。依次去扫描dp[i]这个字符之前的字符,假如发现当前字符大于之前的字符那么比较当前的最长子序列的长度与当前dp[i] + 1的值的大小来更新当前最长递增子序列的长度,当扫描完当前字符i之前的字符那么这个时候说明找到了以当前字符结尾的最长递增子序列的长度,把这个值赋值给dp[i]。
需要注意的是最后还需要扫描一下dp数组,看一下哪一个dp[i]最大,因为我们不知道以哪个字符结尾的字符序列为最长的递增子序列,所以需要扫描一下,最后找到dp[i]的最大值返回,这是与之前的dp数组不同的一个点,以前是返回dp数组的最后一个元素就可以了,因为最后一个元素就是我们需要求解的最优解。这个解法时间复杂度也是O(N2)。
那解法三的思路呢:dp[i]表示的是长度为i的最长递增子序列的末尾的那个数。先初始化dp[0]为数组中第一个元素的值,即dp[0] = 4,定义一个指针p用来记录当前递增子序列的位置,从数组的第二个元素开始比较当前arr[i]与当前指针指向的dp数组的位置的值的大小,假如arr[i] > dp[p]那么说明当前的字符可以构成更长的递增子序列那么我们应该更新dp数组,指针往下移动,然后把当前的arr[i]的值赋值给dp[++p],假如arr[i] < dp[p]说明当前字符不能够构成更长的递增子序列,此时需要进行元素的替换,为什么进行替换呢?因为当前更小的元素更有利于最长递增子序列的贡献,所以扫描dp数组指向位置以及之前的位置假如arr[i] 大于dp[p]那么我们将其dp[p] 替换为arr[i]
最后返回的是指针p指向的位置,注意返回最长递增子序列的长度是p指向的下标,这也是与之前动态规划的一般解法不同的点。更进一步的优化就是这种解法在扫描dp数组的过程中可以通过二分查找来降低时间复杂度,整体从O(N2)降低到O(NlgN)。
代码:
package chapter_08_贪心策略与动态规划;
public class 最长递增子序列 {
static int[] arr = { 4, 2, 3, 1, 5, 6, 4, 8, 5, 9 };
public static void main(String[] args) {
System.out.println(f(arr)); // 输出 6
System.out.println(dp(arr)); // 输出 6
System.out.println(dp1(arr)); // 输出 6
}
// 暴力破解法
private static int f(int[] arr) {
int maxCnt = 0;
for (int i = 0; i < arr.length; i++) {
int p = i;
int cnt = 1;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] > arr[p]) {
cnt++;
p = j;
}
}
// if (cnt>maxCnt){
// maxCnt=cnt;
// }
maxCnt = Math.max(maxCnt, cnt);
}
return maxCnt;
}
static int[] dp = new int[arr.length];
// 解法二
private static int dp(int[] arr) {
dp[0] = 1;
for (int i = 1; i < arr.length; i++) {
int cnt = 1;
for (int j = i - 1; j >= 0; j--) {
if (arr[i] > arr[j]) {
cnt = Math.max(cnt, dp[j] + 1);
}
}
dp[i] = cnt;
}
int ans = -1;
for (int i = 0; i < dp.length; i++) {
ans = Math.max(ans, dp[i]);
}
return ans;
}
// 解法三
/* 在优化之后,可以达到O(NlgN) */
private static int dp1(int[] arr) {
dp = new int[arr.length + 1];
dp[1] = arr[0];// 长度为1的最长递增子序列,初始化为第一个元素
int p = 1;// 记录dp更新的最后位置
for (int i = 1; i < arr.length; i++) {
if (arr[i] > dp[p]) {
dp[p + 1] = arr[i];
p++;
} else {
//扫描dp数组,替换第一个比arr[i]大的dp
//for (int j = 0; j <= p; j++) {
// if (dp[j] > arr[i]) {
// dp[j] = arr[i];
// }
//}
// 二分查找,比上面扫描dp数组耗时少
int indexOfFirstBigger = indexOfFirstBigger(dp, arr[i], 0, p);
if (indexOfFirstBigger != -1)
dp[indexOfFirstBigger] = arr[i];
}
}
return p;
}
/**
* 在递增数组中,从左查找第一个比v大的元素的下标
*/
public static int indexOfFirstBigger(int[] dp, int v, int l, int r) {
while (l <= r) {
int mid = (l + r) >> 1;
if (dp[mid] > v) {
r = mid; // 保留大于v的下标以防这是第一个
} else {
l = mid + 1;
}
if (l == r && dp[l] > v)
return l;
}
return -1;
}
}
动态规划----最长递增子序列问题(LIS)的更多相关文章
- 最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)
最长公共子序列(LCS) [问题] 求两字符序列的最长公共字符子序列 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字 ...
- 最长递增子序列(LIS)(转)
最长递增子序列(LIS) 本博文转自作者:Yx.Ac 文章来源:勇幸|Thinking (http://www.ahathinking.com) --- 最长递增子序列又叫做最长上升子序列 ...
- [51Nod 1218] 最长递增子序列 V2 (LIS)
传送门 Description 数组A包含N个整数.设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可 ...
- 算法之动态规划(最长递增子序列——LIS)
最长递增子序列是动态规划中最经典的问题之一,我们从讨论这个问题开始,循序渐进的了解动态规划的相关知识要点. 在一个已知的序列 {a1, a 2,...an}中,取出若干数组成新的序列{ai1, ai ...
- 求解最长递增子序列(LIS) | 动态规划(DP)+ 二分法
1.题目描述 给定数组arr,返回arr的最长递增子序列. 2.举例 arr={2,1,5,3,6,4,8,9,7},返回的最长递增子序列为{1,3,4,8,9}. 3.解答 ...
- 动态规划 - 最长递增子序列(LIS)
最长递增子序列是动态规划中经典的问题,详细如下: 在一个已知的序列{a1,a2,...,an}中,取出若干数组组成新的序列{ai1,ai2,...,aim},其中下标i1,i2,...,im保持递增, ...
- Python动态规划求解最长递增子序列(LIS)
原始代码错误,移步博客查看O(N^2)及优化的O(N*logN)的实现:每天一道编程题--最长递增子序列
- 动态规划之最长递增子序列(LIS)
在一个已知的序列{ a1,a2,……am}中,取出若干数组成新的序列{ ai1, ai2,…… aim},其中下标 i1,i2, ……im保持递增,即新数列中的各个数之间依旧保持原数列中 ...
- hdu1257最少拦截系统 动态规划(最长递增子序列(LIS))
Problem Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高 ...
随机推荐
- centos6.6 minimal cannot found a valid baseurl for repo :base
网上找了很久,说什么NDS1=8.8.8.8,DNS2=4.2.2.2 也有说改ifcfg-eth0文件的 总之我都改了,但都没用 现在我把我修改的流程发上来 1.https://blog.csdn. ...
- cc.Lable组件,RichText组件,AudioSouce组件的使用
一.cc.Lable组件的使用 1.创建Label的方法 a.通过菜单直接创建Label组件:b.先创建节点,然后在节点上绑定Label组件即可. 2.Label 面板上的属性 String => ...
- python3与mysql交互
1.安装pymysql模块 pip3 install pymysql3 2.pymysql的简单使用: # /usr/bin/env python3 import pymysql class Mysq ...
- 用Python写一个zip文件的密码破解程序
最近在读<python绝技:运用python成为顶级黑客>一书,文中有如何运用Python中zipfile自带的方法破解zip文件.短短的十几行代码就将一个程序实现了.下面给出书中所用的代 ...
- ***报错Class 'Redis' not found in(原创)
报错:Class 'Redis' not found in 这个报错,表明phpredis 扩展没有安装好,而不是redis没有安装 有没有安装成功这个扩展,可以通过phpinfo来查看. 解决问题的 ...
- MongoDB安装之window版本的安装
Windows 平台安装 MongoDB MongoDB 下载 MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 预编译二进制 ...
- C++ 初读vector
vector 向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container).跟任意其它类型容器一样,它能够存放各种类型的对象. Character 高效 C++标准要 ...
- hdu1201 java
题意: 求某人从出生到18岁生日所经过的天数.如果这个人没有18岁生日,就输出-1. 思路: 通过毫秒值计算天数. 利用:来自https://www.cnblogs.com/xiohao/p/5294 ...
- 关于LP Wizard生成Allegro封装无焊盘的解决方案
最近在学Allegro,安装了软件后看网上说LP Wizard可以一键生成Allegro封装,就想去尝尝鲜.毕竟一直都是手动做封装,没怎么用过向导.但是按照网上教程用LP生成了一个封装,发现打开时没有 ...
- 动态规划——Dungeon Game
这又是个题干很搞笑的题目:恶魔把公主囚禁在魔宫的右下角,骑士从魔宫的左上角开始穿越整个魔宫到右下角拯救公主,为了以最快速度拯救公主,骑士每次只能向下或者向右移动一个房间, 每个房间内都有一个整数值,负 ...