最长上升子序列就是给一个序列,求出其中非递减形式的子序列。
O(n²)算法
设数串的长度为n,L[i]为以第i个数为末尾的最长上升子序列的长度,a[i]为数串的第i个数。
L[i]的计算方法为:从前i-1个数中找出满足a[j]<a[i](1<=j<i)条件的最大的L[j],L[i]等于L[j]+1。
动归表达式:
#include <iostream>
#include<algorithm>
#include<vector>
#include<fstream>
#include<iomanip>
#include<map>
#include<sstream>
#include<cmath>
#include<stack>
using namespace std;
int LIS(int*a,int size){
int* d = new int[size]();
int*pre = new int[size]();//用来回溯,记录每个点的前驱节点
int len = 1;
int maxk = 0;//记录最长结尾的下标
//核心算法
// d[i] 表示以a[i]结尾的最长子序列的长度。
// d[]初始化为1。因为子序列最短也是1。
for(int i=0;i<size;i++){
d[i]=1;
pre[i]=i;//如果没有前驱节点,则前驱节点为自身
for(int j=0;j<i;j++){
if(a[i]>a[j] &&(d[j]+1)>d[i]){
d[i] = d[j]+1;
pre[i] = j;
}
}
if(d[i]>len){
len = d[i];
maxk=i;
}
}
//以上结束核心算法,以下为输出序列,LIS的结果序列不唯一,按此方法只能找到结尾数最小的序列
stack<int>st;
int j = maxk,i=maxk;;
do{
j=i;
st.push(a[i]);
i = pre[i];
} while(i!=j);
while(!st.empty()){
cout<<st.top()<<" ";
st.pop();
}cout<<endl;
return len;
}
int main(){
int size;
cin>>size;
int *a = new int[size]();
for(int i=0;i<size;i++){
cin>>a[i];
}
cout<<LIS(a,size);
return 0;
}
测试用例:
input:
9
2 7 1 5 6 4 3 8 9
2 7 1 5 6 4 3 8 9
out:
2 5 6 8 9
5
5
O(nlogn)算法
我们再举一个例子:有以下序列A[]=3 1 2 6 4 5 10 7,求LIS长度。
我们定义一个B[i]来储存可能的排序序列,len为LIS长度。我们依次把A[i]有序地放进B[i]里。(为了方便,i的范围就从1~n表示第i个数)
A[1]=3,把3放进B[1],此时B[1]=3,此时len=1,最小末尾是3
A[2]=1,因为1比3小,所以可以把B[1]中的3替换为1,此时B[1]=1,此时len=1,最小末尾是1
A[3]=2,2大于1,就把2放进B[2]=2,此时B[]={1,2},len=2
同理,A[4]=6,把6放进B[3]=6,B[]={1,2,6},len=3
A[5]=4,4在2和6之间,比6小,可以把B[3]替换为4,B[]={1,2,4},len=3
A[6]=5,B[4]=5,B[]={1,2,4,5},len=4
A[7]=10,B[5]=10,B[]={1,2,4,5,10},len=5
A[8]=7,7在5和10之间,比10小,可以把B[5]替换为7,B[]={1,2,4,5,7},len=5
最终我们得出LIS长度为5。但是,但是!!这里的1 2 4 5 7很明显并不是正确的最长上升子序列。是的,B序列并不表示最长上升子序列,它只表示相应最长子序列长度的排好序的最小序列。这有什么用呢?我们最后一步7替换10并没有增加最长子序列的长度,而这一步的意义,在于记录最小序列,代表了一种“最可能性”。假如后面还有两个数据8和9,那么B[6]将更新为8,B[7]将更新为9,len就变为7。读者可以自行体会它的作用。
因此用O(nlogn)的算法就不知道怎么输出路径了=_ =
#include <iostream>
#include<algorithm>
#include<vector>
#include<fstream>
#include<iomanip>
#include<map>
#include<sstream>
#include<cmath>
#include<stack>
using namespace std;
int LIS(int*a,int size){
int len = 1;
vector<int>v;
//核心算法
for(int i=0;i<size;i++){
if(v.size()==0)v.push_back(a[i]);
else{
int p = lower_bound(v.begin(),v.begin() +len,a[i]) - v.begin();//找到第一个大于等于a[i]元素的坐标
v[p] = a[i];
if(p == len)
len ++;
}
}
return len;
}
int main(){
int size;
cin>>size;
int *a = new int[size]();
for(int i=0;i<size;i++){
cin>>a[i];
}
cout<<LIS(a,size);
return 0;
}
参考博客:http://www.cnblogs.com/GodA/p/5180560.html