数组最长递增子序列(Longest Increasing Sequence)(下称LIS)是个比较常见的题目,算法导论和编程之美上都可以找到。
网上也同样可以找到很多解答和思路,今天自己写了一遍,整理下思路,代码比较简洁,相信比较好理解。
求LIS的思路是动态规划(DP),但是不同的DP思路有着不同的时间复杂度,常见版本时间复杂度为O(n^2),优化的版本为O(n*logn)。
优化版本的精华在于维护一个数组MaxV,其中MaxV[i]记录着长度为i的LIS的结尾元素的最小值,又依据MaxV的单调递增性质,
用二分搜索进行加速,那么就可以把时间复杂度降到O(n*logn)。
具体的思路以后补充完善吧。下面是两种思路对应的程序。
第一个版本,时间复杂度为O(n^2):
//BOP2.16.cpp
//Longest Increasing Sequence (LIS) O(N^2)
#include<iostream>
using namespace std;
const int MaxN=1000;
int a[MaxN];//记录数组信息
int Lis[MaxN];//记录以a[i]结尾的LIS的长度
int MaxLen;//记录LIS长度
int main()
{
//输入数组
int n;
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i];
//初始化Lis每个元素都为1
Lis[i]=1;
}
//初始化
MaxLen=1;
//DP方式遍历Lis[1]-Lis[i-1],然后更新Lis[i]
for(int i=2; i<=n; i++)
{
for(int j=1; j<=i-1; j++)
{
if( a[i] > a[j] && Lis[j]+1 > Lis[i])
Lis[i]=Lis[j]+1;
if( a[i] == a[j])
Lis[i]=Lis[j];
}
if(Lis[i] > MaxLen) MaxLen=Lis[i];
}
//for(int i=1; i<=n; i++) cout<<Lis[i]<<" ";
//cout<<endl;
//输出LIS的长度MaxLen
cout<<MaxLen<<endl;
system("pause");
return 0;
}
第二个版本,时间复杂度为O(n*logn):
//BOP2.16.cpp
//Longest Increasing Sequence (LIS) 0(n*lgn)
#include<iostream>
using namespace std;
const int MaxN=1000;
int a[MaxN];//记录数组信息
int Lis[MaxN];//记录以a[i]结尾的LIS的长度
int MaxV[MaxN];//记录长度为i的LIS的结尾元素的最小值
int MaxLen;//记录LIS长度
//二分查找,返回a[i]在MaxV中对应的位置 ,可以用p=q来测试
int bsearch(int p, int q, int num)
{
if(p > q) return p;
int r=(p + q)/2;
if(num == MaxV[r]) return r;
if(num < MaxV[r]) return bsearch(p, r-1, num);
if(num > MaxV[r]) return bsearch(r+1, q, num);
}
int main()
{
//输入数组
int n;
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i];
//初始化Lis每个元素都为1
Lis[i]=1;
}
//初始化
int pos;//记录a[i]在MaxV中对应的位置
MaxV[1]=a[1];
MaxLen=1;
//DP方式从第二个元素开始遍历, 每次和MaxV中元素比较,而不是Lis
for(int i=2; i<=n; i++)
{
pos=bsearch(1, MaxLen, a[i]);
Lis[i]=pos;
if(pos > MaxLen)
{
//Lis[i]=++MaxLen;
MaxV[++MaxLen]=a[i];
}
else
{
//Lis[i]=pos;
MaxV[pos]=a[i];
}
}
//for(int i=1; i<=n; i++) cout<<Lis[i]<<" ";
//cout<<endl;
//输出LIS的长度MaxLen
cout<<MaxLen<<endl;
system("pause");
return 0;
}
测试用例:
Input:
8
1 -1 2 -3 4 -5 6 7
Output:
4