您的位置:首页 > 编程语言

最长递增子序列

2014-05-25 00:37 162 查看

最长递增子序列

问题:

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

解法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):最长递增子序列
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息