算法——最长递增子序列(LIS)

来源:互联网 发布:除湿袋真的有用吗 知乎 编辑:程序博客网 时间:2024/06/01 11:39

最长递增子序列描述的是这样一个问题——给定一个混乱序列a1,a2,...an,要求找到他的一个子序列ai1,ai2,...aik,这k个数的下标严格增大,且k值达到最大。这道题根据需要的不同有不同的做法,首先来看一个比较简单的需要——求这个LIS的长度。

求长度可以直接用栈的方法,初始时len = 1,从首元素开始压栈,第i个元素如果大于栈顶元素就压栈且len++,如果第i个元素小于栈顶元素,则查找栈中比该元素小的第一个元素并替换,当遍历完整个数组后,len的值就是LIS的长度,但是栈中的内容并不是LIS,这也是这种方法的局限性。

那么怎样才能做到既输出长度又输出整个LIS呢?

首先要知道栈的方法由于有替换的过程,但替换来的不一定能构成LIS,但是可以作为一个新的LIS的“备选”,当备选的长度超过现有的长度时,我们就可以用备选者替换当前的LIS。

可以发现,每次添加一个元素,要么作为最小的元素新开启一个LIS,要么作为递增的大元素加在LIS的后面,当它成为中间元素时,我们则需要找到一个长度s,使这个元素恰好在LIS(s)和LIS(s-1)的末尾元素之间,那么他就可以作为LIS(s)的新末尾元素。

而说到这里,其实这就是一个关于“位置”的设计,相同位置的取舍就在于哪一个元素所在的LIS更长。代码如下,pos数组记录的就是每个元素的的前一个元素的位置,而MTS(i)表示长度为i的LIS的末尾元素的位置。代码最后还对pos打了表,方便看出它的作用。

#include <iostream>using namespace std;int main(){int a[10] = { 0,1,2,3,8,9,4,5,6,10 }, MTS[10] = { 0 }, pos[10] = { 0 };int len = 1;MTS[1] = 1;pos[1] = 0;for (int i = 2; i < 10; i++){if (a[MTS[1]] > a[i]){pos[i] = 0;MTS[1] = i;}else if (a[MTS[len]] <= a[i]){pos[i] = MTS[len];len++;MTS[len] = i;}else{int mid, up, low;low =1;up =len;while (low <= up){mid = (low + up) / 2;if (a[MTS[mid]] > a[i])up = mid-1 ;elselow = mid+1;}pos[i] = MTS[mid];MTS[mid + 1] = i;}}int pt = MTS[len];for (int i = 1; i <= len; i++){cout << a[pt]<<",";pt = pos[pt];}cout << endl;cout << len << endl;for (int i = 1; i < 10; i++)cout << pos[i] << ",";cout << endl;for (int i = 1; i < 10; i++)cout << a[i] << ",";return 0;}


0 0
原创粉丝点击