传送门

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+10, 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 keyint mid;while(r-l>1){ //r is illegalmid=(l+r)>>1;if(f[mid]<y){l=mid;}else r=mid;}return l;
}int main(){//freopen("in", "r", stdin);int N, ans=0;scanf("%d", &N);for(int i=1; i<=N; i++) scanf("%d", A+i);f[0]=0;for(int i=1; i<=N; i++) f[i]=oo;for(int i=1; i<=N; i++){int tmp=binary_search(A[i], 0, ans+1)+1;ans=max(ans, tmp);f[tmp]=min(f[tmp], A[i]);}printf("%d\n", ans);return 0;
}

优化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+5, MAX_N=1e6+5;
int ma[MAX_M<<2];
void insert(int id, int L, int R, int pos, int v){if(L==R){ma[id]=v;}else{int mid=(L+R)>>1;if(pos<=mid){insert(id<<1, L, mid, pos, v);}else{insert(id<<1|1, mid+1, R, pos, v);}ma[id]=max(ma[id<<1], ma[id<<1|1]);}
}
int query(int id, int L, int R, int l, int r){if(l<=L && R<=r) return ma[id];int mid=(L+R)>>1;int res=0;if(l<=mid){res=max(res, query(id<<1, L, mid, l, r));}if(r>mid){res=max(res, query(id<<1|1, mid+1, R, l, r));}return res;
}
int a[MAX_N];
int main(){//freopen("in", "r", stdin);int N;//single case, leave out initializationscanf("%d", &N);int rb;for(int i=0; i<N; i++) scanf("%d", a+i), rb=max(rb, a[i]);int ans=0;for(int i=0; i<N; i++){int tmp=query(1, 0, rb, 0, a[i]-1)+1;ans=max(ans, tmp);insert(1, 0, rb, a[i], tmp);    //over-head
    }printf("%d\n", ans);return 0;
}

这种优化方法具体地可以称作数据结构优化
这种方法的弊端:

代码量大、常数大

另外尤其注意插入操作

insert(1, 0, 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+5, MAX_M=1e5+5;
int bit[MAX_M], M;
int a[MAX_N];
int query(int i){int res=0;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=0;for(int i=0; i<N; i++)  scanf("%d", a+i), M=max(M, a[i]);int ans=0;int tmp;for(int i=0; i<N; i++){tmp=query(a[i]-1)+1;modify(a[i], tmp);ans=max(ans, tmp);}printf("%d\n", ans);return 0;
}

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.

转载于:https://www.cnblogs.com/Patt/p/4693665.html

The Longest Increasing Subsequence (LIS)相关推荐

  1. Dynamic Programming之Longest Increasing Subsequence (LIS)问题

    Longest Increasing Subsequence(LIS)问题是一类常见的可使用Dynamic Programming解决的算法问题.这个问题是指在一个数字序列中,找到最大个数升序排列的子 ...

  2. Longest Increasing Subsequence(LIS入门dp)

    http://poj.org/problem?id=2533 Longest Ordered Subsequence Time Limit: 2000MS Memory Limit: 65536K D ...

  3. 【Lintcode】076.Longest Increasing Subsequence

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

  4. [Swift]LeetCode673. 最长递增子序列的个数 | Number of Longest Increasing Subsequence

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

  5. C++longest increasing subsequence 最长递增子序列的实现之二(附完整源码)

    C++longest increasing subsequence 最长递增子序列的实现 C++longest increasing subsequence 最长递增子序列的的实现完整源码(定义,实现 ...

  6. C++longest increasing subsequence 最长递增子序列的实现之一(附完整源码)

    C++longest increasing subsequence 最长递增子序列的实现 C++longest increasing subsequence 最长递增子序列的的实现完整源码(定义,实现 ...

  7. leetcode(300)—— Longest Increasing Subsequence(最长递增子序列)

    参考 Python 解法: 动态规划 -- 最长递增子序列(LIS) 原题位置:Longest Increasing Subsequence | LeetCode OJ 题目的说明: 严格递增: 子序 ...

  8. HPU第三次积分赛-D:Longest Increasing Subsequence(DP)

    Longest Increasing Subsequence 描述 给出一组长度为n的序列,a1​,a2​,a3​,a4​...an​, 求出这个序列长度为k的严格递增子序列的个数 输入 第一行输入T ...

  9. leetcode 300. Longest Increasing Subsequence | 300. 最长递增子序列(动态规划)

    题目 https://leetcode.com/problems/longest-increasing-subsequence/ 题解 难得有官方题解的一道题. 参考:https://leetcode ...

最新文章

  1. HBuilderX 连接电脑的模拟器问题
  2. 微博 用户画像_微博/抖音/快手/小红书/B站内容营销和粉丝画像研究
  3. 使用泛型实现单例提供者(原创翻译)
  4. java有 号_JAVA揭竿而起总要有名号
  5. 会写高考作文的AI,内含17亿参数、2亿数据、1万行代码
  6. mysql string agg_postgresql – 如何使array_agg()像mySQL中的group_concat()一样工作
  7. ai中位图转矢量图(扩展与扩展外观)
  8. dcn网络与公网_DCN网
  9. android没有adm_这可能是安卓平台上最好的下载器:ADM
  10. Neo4J入门笔记[2]---Neo4J GDS 图数据科学库
  11. 在Windows下安装BIND作为DNS服务器
  12. EMC常见术语-dB、dBm、dBw以及如何计算
  13. USB接口测试工装研究
  14. 自媒体达人早已月入过万,为何同是自媒体人却依旧没收益
  15. 文件的上传和下载(一)
  16. 初中计算机活动策划方案,信息技术中考备考方案
  17. 运筹系列16:routing模型之VRP问题
  18. 通过xml生成java Bean
  19. 结构化P2P网络——DHT网络原理
  20. 手机python编程软件西西网-14.python-CS编程

热门文章

  1. 开发板、Windows、Ubuntu三者互联——韦东山嵌入式Linux学习笔记08
  2. springboot单元测试中@Autowired自动注入的类一直是null
  3. DM8168学习--USB的over-current 问题总结
  4. DM8168_ETV_V1.1开发板mount主机常见问题
  5. 内核程序实现多文件的调用
  6. 【译】Google's AutoML: Cutting Through the Hype
  7. 【译】The missing explanation of Proof of Stake Version 3
  8. php 云技术,什么叫云技术?
  9. 骁龙660是32位还是64位_骁龙660是32位还是64位_都是搭载骁龙660处理器 这三款国产手机如何选...
  10. 怎么钢枪_这样玩《和平精英》有手就能上皇冠?教你玩吃鸡怎么涨KD