最长递增子序列



问题:

求一个一维数组中最长递增子序列的长度。

解法1:

非常明显用动态规划的算法,选取以下的阶段(这样的选法极为常见),可使阶段间的关系具有无后效性。

阶段:在全部以元素k结尾的子数组中,选出当中的最长递增子序列,k=1,2...n。

状态:以元素k结尾的最长递增子序列中仅仅有一个最长的递增子序列。


决策:决定元素k结尾的最长递增子序列有k-1种获取的途径,前面以不论什么一个元素结尾的最长递增子序列都可能成为其的一部分。


这种时间复杂度为O(n^2),空间复杂度为O(n)





解法2:


动态规划的时间复杂度一般与空间复杂度同样,由于决定下一个阶段的全部条件我们已存储到dp中,在处理过程中我们能够对已得到的这些条件进行处理来减少时间复杂度。而这里时间复杂度竟比空间复杂度高了O(n),说明还有能够继续优化的空间。




我们能够统计前面全部阶段的最长递增子序列的长度,将长度同样的最长递增子序列分到同一组中,并仅仅记录它们最大元素的最小值MaxV[长度值],假设阶段k的元素大于长度为i最长递增子序列的这个最小值MaxV[i],那么阶段k的最长递增子序列的长度至少为i+1。




而我们发现统计出的MaxV数组具有非常好的递增关系(动态规划都是用来解决最优化问题,我们总能通过优化关系对之前统计出的结果进行排序),即假设i<j,那么有MaxV[i]<MaxV[j],最后用二分查找法来阶段k的元素在MaxV数组中的位置就可以。


证明:反证法,如果当i<j<=k,有MaxV[i]>=MaxV[j],那么存在一个长度为i的递增序列a1a2...ai,且ai是计算到阶段k时全部长度为i的递增序列中最大元素的最小值,以及长度为j的序列b1b2...bj且ai>=bj,因为i<j长度j的序列b1b2...bj包括一个子序列b1b2...bi,且bi<bj<=ai,即长度为i的递增子序列最大元素的最小值不是ai,矛盾。

代码:


#include<iostream>
#include<cstring>
#include<algorithm>
#define MAX 1000

using namespace std;
int dp[MAX];

int LIS1(int Array[],int n)
{
int i,j;
int max=-0xFFFFFFF;
for(i=1;i<=n;i++)
{
dp[i]=1;
for(j=1;j<i;j++)
{
if(Array[i]>Array[j]&&dp[j]+1>dp[i])
dp[i]=dp[j]+1;
}
if(dp[i]>max)
max=dp[i];
}
return max;
}

int MinValue(int Array[],int n)
{
int min=0xFFFFFFF;
for(int i=1;i<=n;i++)
if(min>Array[i])
min=Array[i];
return min;
}

int LIS2(int Array[],int n)
{
int MaxV[MAX];//记录数组中的递增序列信息
MaxV[0]=MinValue(Array,n)-1;//边界值,数组中的最小值
MaxV[1]=Array[1];//边界值,数组中的第一个值

for(int i=1;i<=n;i++)//初始化最长递增序列的信息
dp[i]=1;

int nMaxLIS=1;//数组最长递增子序列的初始值

for(int i=1;i<=n;i++)
{ //遍历历史最长递增序列的信息
int j;
for(j=nMaxLIS;j>=0;j--)
{
if(Array[i]>MaxV[j])
{
dp[i]=j+1;
break;
}
}
//假设当前最长序列大于最长递增序列长度,更新最长信息
if(dp[i]>nMaxLIS)
{
nMaxLIS=dp[i];
MaxV[dp[i]]=Array[i];
}
else if(MaxV[j]<Array[i]&&Array[i]<MaxV[j+1])
MaxV[j+1]=Array[i];
}

return nMaxLIS;
}

int main(int argc,char *argv[])
{
int Array[MAX];
int i,n;
cout<<"Please input n= ";
cin>>n;
cout<<"Please input Array values:"<<endl;
for(i=1;i<=n;i++)
cin>>Array[i];
cout<<"LIS1: "<<LIS1(Array,n)<<endl;
memset(dp,0,sizeof(dp));
cout<<"LIS2: "<<LIS2(Array,n)<<endl;

return 0;
}


解法3:

原来写过一篇二分搜索的解法,时间复杂度O(n*lgn):​​最长递增子序列​