LIS

来源:互联网 发布:单片机开发板怎么用 编辑:程序博客网 时间:2024/05/01 12:00

1、定义

LIS(Longest Increasing Subsequence),最长上升子序列:对给定的序列A = {a1, a2, a3,...an},找出最长的子序列B = {ai1, ai2, ai3,...,aim}使得i1<i2<i3<...<im && ai1<ai2< ai3<aim。

2、解法

(1)dp

思路:设dp[i]是以A[i]为末尾的上升子序列的长度,则

dp[i] = max(dp[j]) + 1 (j < i && a[j] < a[i])

初始时,dp[i] = 1。lis = max(dp[i])。这个算法的时间复杂度为O(n2),空间复杂度为O(n)。

记录路径:使用辅助数组path[i]表示以num[i]结尾的上升子序列中num[i]的前一个数的id。

(2)CMI算法

思路:CMI算法的精髓是用二分,使时间复杂度降到O(nlgn)。该算法维护一个递增的栈s,对于当前元素a[i]:

  • a[i] > top(s),则s.push(a[i]);
  • a[i] <= top(s)时,则对s二分搜索一个j,使得s[j] < a[i] <= s[j + 1],更新s[j + 1] = a[i];
假设A = {3,4,1,2,3},模拟:

i = 0, a[i] = 3,s = {3};

i = 1, a[i] = 4, a[i] > top(s),则s = {3,4}

i = 3, a[i] = 1, a[i] <= top(s),二分找到j = 0,s = {1,4}

i = 4, a[i] = 2, a[i] <= top(s),二分找到j = 1,s = {1,2}

i = 5, a[i] = 3, a[i] > top(s), 则s = {1,2,3}

通过模拟可以看到,a[i] <= top(s)的操作使得栈更具容纳性。

记录路径:用path[i]记录a[i]的前一个id,每次在找到s的插入点时,设置a[i]的path[i]。如果二分搜索到的更新点是j + 1,则path[i] = s[i]。

该算法时间复杂度为O(nlgn),空间复杂度为O(n)。

3、扩展

Beautiful People n个人a,每个人都有t和b属性,要求找到任意顺序的最长子序列,使得t[i] < t[j] && b[i] < b[j]:

这题题意是任意顺序的最长子序列,跟经典的LIS要求的下标递增不同。先对a按t递增、t相同时按b递减排序,然后对b进行CMI操作。这样排序的好处在:每次在二分并更新s[j + 1]时,可以保证此时的t比s[j]的t要大。

Nested Dolls 套娃,n个娃娃,每个有长w和高h属性,问最少能形成几个套娃。对w按从大小到、h按从小到大排序。然后对h做LIS,最后的长度便为最少的个数。按w从大到小排序,保证了每次加一个的w都比前一个要小,所以肯定能放下;而当w相等时,按h从小到大排序,保证了当连续两个w相等的娃娃比较时,肯定是不能嵌套的。在栈中的每个元素都是独立的一个套娃最里层的h。

Wooden Sticks:跟Netsted Dolls相同,不过它的要求是w1 <= w2 && h1 <= h2,排序的时候w相等时按h递归排就行了。

0 0
原创粉丝点击