The Longest Increasing Subsequence (LIS)
The task is to find the length of the longest subsequence in a given array of integers such that all elements of the subsequence are sorted in ascending order. For example, the length of the LIS for { 15, 27, 14, 38, 26, 55, 46, 65, 85 } is 6 and the longest increasing subsequence is {15, 27, 38, 55, 65, 85}.
In this challenge you simply have to find the length of the longest strictly increasing sub-sequence of the given sequence.
Input Format
In the first line of input, there is a single number N. In the next N lines input the value of a[i].
Constraints
1 ≤ N ≤ 106
1 ≤ a[i] ≤ 105
Output Format
In a single line, output the length of the longest increasing sub-sequence.
Sample Input
5
2
7
4
3
8
Sample Output
3
Explanation
{2,7,8} is the longest increasing sub-sequence, hence the answer is 3 (the length of this sub-sequence).
十分重要的基础DP问题,是学习用各种方式优化DP的一个很好的例子。
设DP[i]表示以a[i]结尾的LIS的长度,则状态转移方程为
DP[1]=1
DP[i] = max{DP[j], j<i && a[j]<a[i]} + 1, i>1
暴力求解复杂度为O(N^2)。
优化1
考虑函数f(L):长度为 L 的 Increasing Sequence (IS) 的最小结尾, 显然f(L)是单调递增的。
从左到右扫描数组,维护动态的f(L)。
再看上面的DP方程,考虑我们维护的这个函数f(L)如何加速DP状态转移时所需的查询。
显然我们以a[i]为key,二分查询f(L),得到max{L, f(L)<a[i]}
复杂度降为O(N*logN)
这一类DP优化方法可归纳为 加速DP Query
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_N=1e6+, oo=1e6;
int A[MAX_N], f[MAX_N];
//二分法: binary_search(y, f())
//设有一个单调(不要求严格单调)的函数f()
//可以在O(log N)的时间内求解以下问题:
//给定y,求满足f(x)<=y/f(x)>=y的最大/最小的x
// int binary_search(int y, int l, int r){ //y is the key
int mid;
while(r-l>){ //r is illegal
mid=(l+r)>>;
if(f[mid]<y){
l=mid;
}
else r=mid;
}
return l;
} int main(){
//freopen("in", "r", stdin);
int N, ans=;
scanf("%d", &N);
for(int i=; i<=N; i++) scanf("%d", A+i); f[]=;
for(int i=; i<=N; i++) f[i]=oo;
for(int i=; i<=N; i++){
int tmp=binary_search(A[i], , ans+)+;
ans=max(ans, tmp);
f[tmp]=min(f[tmp], A[i]);
}
printf("%d\n", ans);
return ;
}
优化2
具体地说,可把第一种优化方式称作单调性优化。我们再考虑一种优化方式。
仍观察DP方程 DP[i]=max{DP[j], j<i && a[j]<a[j]} + 1
我们考虑用数据结构加速查询,查询的键值 (key) 是a[i], ( 第一个条件 i<j 是自然满足的), 对应的value就是max{dp[j], a[j]<=a[i]}。我们把用于查询的 <key, value> 维护成线段树。其中key就是节点的L, R, 这样<key, value> 查询就是RMQ(Range Maximum Query)。
这样可以优化到O(NlogM),M是a[i]的最大值,可通过NlogN的离散化优化到NlogN (M比N大很多时才有必要离散化)。
#include<bits/stdc++.h>
using namespace std;
const int MAX_M=1e5+, MAX_N=1e6+;
int ma[MAX_M<<];
void insert(int id, int L, int R, int pos, int v){
if(L==R){
ma[id]=v;
}
else{
int mid=(L+R)>>;
if(pos<=mid){
insert(id<<, L, mid, pos, v);
}
else{
insert(id<<|, mid+, R, pos, v);
}
ma[id]=max(ma[id<<], ma[id<<|]);
}
}
int query(int id, int L, int R, int l, int r){
if(l<=L && R<=r) return ma[id];
int mid=(L+R)>>;
int res=;
if(l<=mid){
res=max(res, query(id<<, L, mid, l, r));
}
if(r>mid){
res=max(res, query(id<<|, mid+, R, l, r));
}
return res;
}
int a[MAX_N];
int main(){
//freopen("in", "r", stdin);
int N;
//single case, leave out initialization
scanf("%d", &N);
int rb;
for(int i=; i<N; i++) scanf("%d", a+i), rb=max(rb, a[i]);
int ans=;
for(int i=; i<N; i++){
int tmp=query(, , rb, , a[i]-)+;
ans=max(ans, tmp);
insert(, , rb, a[i], tmp); //over-head
}
printf("%d\n", ans);
return ;
}
这种优化方法具体地可以称作数据结构优化。
这种方法的弊端:
代码量大、常数大
另外尤其注意插入操作
insert(, , rb, a[i], tmp);
这个操作往往并没有增大 (0, a[i]) 区间的最大值,但是这里每次更新都要深入到叶子节点,不能不说是一种冗余 (树状数组就很好地避免了这种冗余)。
其实没必要用线段树来维护查询,注意到每次查询的区间左端都是0,可用树状数组(Binary Indexed Tree, BIT)代替。
具体而言,BIT的每个节点维护对应区间的最大值,更新与查询的复杂度都是O(logM)总体复杂度为O(NlogM)。
#include<bits/stdc++.h>
using namespace std;
const int MAX_N=1e6+, MAX_M=1e5+;
int bit[MAX_M], M;
int a[MAX_N];
int query(int i){
int res=;
while(i){
res=max(res, bit[i]);
i-=i&-i;
}
return res;
}
void modify(int i, int v){
while(i<=M){
if(bit[i]>=v) return;
bit[i]=v;
i+=i&-i;
}
}
int main(){
//freopen("in", "r", stdin);
int N;
scanf("%d", &N);
M=;
for(int i=; i<N; i++) scanf("%d", a+i), M=max(M, a[i]);
int ans=;
int tmp;
for(int i=; i<N; i++){
tmp=query(a[i]-)+;
modify(a[i], tmp);
ans=max(ans, tmp);
}
printf("%d\n", ans);
return ;
}
BIT代码短、速度快,可与二分查询媲美。
P.S. 写这篇随笔时,专门考虑了如何用BIT维护prefix maximum。最初的想法是将prefix maximum维护(表示)成prefix sum,发现行不通。结论是自己学得太死,全不知变通。其实BIT和线段树一样,两者的节点表示的都是区间,可以说没有本质差别。而我只知道BIT可维护prefix sum, 但对于BIT是如何维护prefix sum的却不了然,所以不能灵活应用。
Given a table of elements, it is sometimes desirable to calculate the running total of values up to each index according to some associative binary operation (addition on integers, for example). Fenwick trees provide a method to query the running total at any index, in addition to allowing changes to the underlying value table and having all further queries reflect those changes.
The Longest Increasing Subsequence (LIS)的更多相关文章
- [tem]Longest Increasing Subsequence(LIS)
Longest Increasing Subsequence(LIS) 一个美丽的名字 非常经典的线性结构dp [朴素]:O(n^2) d(i)=max{0,d(j) :j<i&& ...
- 300. Longest Increasing Subsequence(LIS最长递增子序列 动态规划)
Given an unsorted array of integers, find the length of longest increasing subsequence. For example, ...
- LeetCode -- Longest Increasing Subsequence(LIS)
Question: Given an unsorted array of integers, find the length of longest increasing subsequence. Fo ...
- [LintCode] Longest Increasing Subsequence 最长递增子序列
Given a sequence of integers, find the longest increasing subsequence (LIS). You code should return ...
- [Algorithms] Longest Increasing Subsequence
The Longest Increasing Subsequence (LIS) problem requires us to find a subsequence t of a given sequ ...
- 【Lintcode】076.Longest Increasing Subsequence
题目: Given a sequence of integers, find the longest increasing subsequence (LIS). You code should ret ...
- LintCode刷题笔记--Longest Increasing Subsequence
标签: 动态规划 描述: Given a sequence of integers, find the longest increasing subsequence (LIS). You code s ...
- 最长上升子序列 LIS(Longest Increasing Subsequence)
引出: 问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…< ...
- [LeetCode] Longest Increasing Subsequence 最长递增子序列
Given an unsorted array of integers, find the length of longest increasing subsequence. For example, ...
随机推荐
- dos系统下mysql常用命令
show table status;//查看所有表状态,通过这个命令可以得知表的创建时间和最后更新时间,以及该表是基表还是视图以及是什么表引擎等信息. show table status from d ...
- zabbix错误记录
zabbix部署好,在使用一段时间后,出现了不少报错,在此简单做一记录.1)Zabbix监控界面报错“Lack of free swap space”解决公司线上部署的zabbix3.0的监控界面首页 ...
- js文本框提示和自动完成
1.模仿大型网站自动提示,就是输入“苹果”,在水果类中搜索,html代码如下: <div id="searchTips" style="display:none;w ...
- Spring之AOP
package org.zln.module.test3_aop.interceptor; import org.aspectj.lang.ProceedingJoinPoint; import or ...
- 23Mybatis_根据订单商品数据模型的练习对resultMap和resulttype的总结
resultType: 作用: 将查询结果按照sql列名pojo属性名一致性映射到pojo中. 场合: 常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用re ...
- C语言 函数理解(以数组做参数)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> int run(int *p){ // ...
- OpenGL 4.3配置教程
OpenGL 4.3配置教程 下载开发包 需要下载的开发包主要包含如下几个组件:freeglut+glew+ OpenGL.Development.Cookbook+源码+GLM+SOIL. Open ...
- OAF 中对文字实现html效果及对超级长文本实现默认换行,对只读的messageTextInput中的内容自动换行
今天遇到一个需求,客户注册页面客户化了一个超级长的注册须知,内容很多.但是样式相对又要做起来好看点. 注册须知的内容使用多个message拼接而成. 老大说rawText支持html样式,于是我想到了 ...
- 一个背景图实现自定义spinner样式
如下界面:由一个spinner两个EditText一个Button实现,为了保持界面的统一性,需要把默认的spinner样式改成类似下面的样式. xml文件布局如下图 这里用一个LinerLayout ...
- linux(centos 6.4)下安装php memcache服务端及其客户端(详细教程)
前言 在搭建个人博客时,由于没有使用任何框架,纯手工code前台和后台,导致遇到许多问题,其中一个问题就是mysql连接导致的页面相应速度异常低.在查询各种途径后,只能考虑使用memcache缓存.在 ...