传送门

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)的更多相关文章

  1. [tem]Longest Increasing Subsequence(LIS)

    Longest Increasing Subsequence(LIS) 一个美丽的名字 非常经典的线性结构dp [朴素]:O(n^2) d(i)=max{0,d(j) :j<i&& ...

  2. 300. Longest Increasing Subsequence(LIS最长递增子序列 动态规划)

    Given an unsorted array of integers, find the length of longest increasing subsequence. For example, ...

  3. LeetCode -- Longest Increasing Subsequence(LIS)

    Question: Given an unsorted array of integers, find the length of longest increasing subsequence. Fo ...

  4. [LintCode] Longest Increasing Subsequence 最长递增子序列

    Given a sequence of integers, find the longest increasing subsequence (LIS). You code should return ...

  5. [Algorithms] Longest Increasing Subsequence

    The Longest Increasing Subsequence (LIS) problem requires us to find a subsequence t of a given sequ ...

  6. 【Lintcode】076.Longest Increasing Subsequence

    题目: Given a sequence of integers, find the longest increasing subsequence (LIS). You code should ret ...

  7. LintCode刷题笔记--Longest Increasing Subsequence

    标签: 动态规划 描述: Given a sequence of integers, find the longest increasing subsequence (LIS). You code s ...

  8. 最长上升子序列 LIS(Longest Increasing Subsequence)

    引出: 问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…< ...

  9. [LeetCode] Longest Increasing Subsequence 最长递增子序列

    Given an unsorted array of integers, find the length of longest increasing subsequence. For example, ...

随机推荐

  1. Android Activity的生命周期

    一.为什么要了解Activity的生命周期 activity is directly affected by its association withother activities, its tas ...

  2. Elasticsearch 相关名词理解

    Cluster包含多个node,Indices不应该理解成动词索引,Indices可理解成关系数据库中的databases,Indices可包含多个Index,Index对应关系数据库中的databa ...

  3. Java连接Elasticsearch集群

    package cn.test; import java.net.InetAddress; import java.net.UnknownHostException; import org.elast ...

  4. linux添加时间提示符

    给PS1添加\t [root@lanny ~]# echo $PS1 [\u@\h \W]\$ [root@lanny ~]# export PS1="[\u@\h \W\t]\$" ...

  5. jQuery Event.stopPropagation() 函数详解

    stopPropagation()函数用于阻止当前事件在DOM树上冒泡. 根据DOM事件流机制,在元素上触发的大多数事件都会冒泡传递到该元素的所有祖辈元素上,如果这些祖辈元素上也绑定了相应的事件处理函 ...

  6. 那些年我们写过的T-SQL(下篇)(转)

    原文:http://www.cnblogs.com/wanliwang01/p/TSQL_Base04.html   下篇的内容很多都会在工作中用到,尤其是可编程对象,那些年我们写过的存储过程,有木有 ...

  7. 每日一SQL-善用DATEADD和DATEDIFF

    转自:http://www.dotblogs.com.tw/lastsecret/archive/2010/10/04/18097.aspx 上個星期去Tech-Day聽了幾場有趣的課,其中一堂是楊志 ...

  8. Opencv step by step - 加载视频

    刚买了本 "学习Opencv" 这本书,慢慢看起来. 一开始就是加载视频了.当然了,首先你要有个视频 从这里下载了一个: tan@ubuntu:~$ wget http://www ...

  9. python中的模块

    用python其实好久了,但是一直没有特别系统的学习过,当年迅速上手,也写了好多代码了,零零散散也学了很多知识点.到了把它们串起来的时候了.尝试记录一下系统整理的知识点,先从“模块”说起. 为什么需要 ...

  10. java 中Handler 和Runnable 的使用 异步发送消息 转

    public class MainActivity extends Activity { TextView text1, text2; Button button; Thread th; @Overr ...