学习动态规划问题(DP问题)中,其中有一个知识点叫最长上升子序列(longes increasing subsequence),也可以叫最长非降序子序列,简称LIS。简单说一下自己的心得。
关于这个问题,之所以能用动态规划来解决,只因为其满足动态规划的条件,当前解可以由上一个阶段的解推出
建立状态转移方程,就能把大的问题转化为小的问题,进而逐渐缩小规模,直至解决。
分析:设有n个数的最长上升子序列长度为f(n),我们要求f(n),可以先求f(n-1),再对n做判断,反复判断,直至n=1,我们可以知道f(1)=1;
具体做法:a[n]储存n个数,维护一个一维数组dp[I],dp[I]表示前I个数的LIS,每加入一个数a[I],找出满足(j<I&&a[j]<a[I])的最大的dp[j],即找出当前能让a[I]加入的最长的序列,然后加一,即dp[I]=max(dp[I],dp[j]j<I&&a[j]<a[I])+1.
代码如下:(有重复元素)
上述算法通过两个循环不难看出复杂度为O(n2),可以通过优化使复杂度降低到O(nlogn),方法时将内循环搜索改为二分查找,这样二分查找复杂度为logn,可以大大提高速度。但二分需有序,因此我们换一种dp数组的表示意义。
具体做法:同样是a[i]储存n个数,不同的是维护的数组dp1[i]表示的是LIS长度为i的序列最小的结尾值,最后输出dp数组的长度即为所求。比较见下表:
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] |
1 | 7 | 3 | 5 | 9 | 4 | 8 |
dp[0] | dp[1] | dp[2] | dp[3] | dp[4] | dp[5] | dp[6] |
1 | 2 | 2 | 3 | 4 | 3 | 4 |
dp1[0] | dp1[1] | dp1[2] | dp1[3] | dp1[4] | dp1[5] | dp1[6] |
1 | 3 | 4 | 8 | 0 | 0 | 0 |
代码如下:(有重复元素)
(无重复元素):
注意有无重复元素代码的红色部分。
upper_bound与lower_bound的比较
首先,最形象的一句话:
upper_bound(i) 返回的是键值为i的元素可以插入的最后一个位置(上界)
lowe_bound(i) 返回的是键值为i的元素可以插入的位置的第一个位置(下界)。
举例说明
1 2 4 5 中插入3都是返回位置3
1 3 3 4 中插入3upper_bound就返回位置4而lower_bound返回的是位置2