最长单调递增子序列(时间复杂度O(nlogn))

写在前面:仅为个人代码/总结,未必标准,仅供参考!如有错误,还望指出交流,共同进步!

最长单调递增子序列

【题目描述】
找出由n个数组成的序列中的最长单调递增子序列及其长度。

【O(n*n)算法解题思路】
思路一:用数组 b[0:n-1]记录以a[i] (0≤i 思路二:将原问题转化为求最长公共子序列问题,求原序列和升序排序后序列的最长公共子序列。

【O(nlogn)算法解题思路】
用归纳解此问题。归纳假设是:已知计算序列a[0:i-1] (i 定义P:(0≤i 具体解题步骤如下:
在由i-1到i的循环中,
①当a[i]>b[k]时,k=k+1, b[k]=a[i],否则k值不变。
②当a[i]≤b[k]时,如果a[i] ③对于记录最长递增子序列,可在求得每一个b[j]时用pre[b[i]]记录b[j]的前驱数据为b[j-1],即排在b[j]前面的序列a[0:i-1]的所有长度为j-1的递增子序列中的最小结尾元素值。

【解题过程遇到的问题】
问:为什么步骤的第②点中当b[j-1]==a[i]时要保持不变?
答:来个例子,比如序列1 2 3 2,当i递增到3时,发现a[i]==b[2],但如果将b[3]改为a[i],这会得到2是长度为3的递增子序列中的最小结尾元素值,这虽然不影响求得原序列的最长单调递增子序列长度为3,但如果要根据数组b来记录最长单调递增子序列则行不通,因为将b[3]改为2后已不满足b[k]的定义,2并不是a[0…3]中长度为3的单调递增子序列的最小结尾值,2,2不构成递增关系。

【样例解答】
最长单调递增子序列(时间复杂度O(nlogn))_第1张图片
最长单调递增子序列(时间复杂度O(nlogn))_第2张图片
【实现代码】

#include 
using namespace std;
int a[101]={0};
int b[101]={0};
int pre[101]={0};
int n;
stack <int> S;
//k是序列a[0:i]的最长递增子序列的长度
//b[k]是序列a[0:i]中所有长度为k的递增子序列中的最小结尾元素值
//假若w等于某一个b[k],则pre[w]存排在w前面的所有长度为k-1的递增子序列中的最小结尾元素值
int binary(int i,int k)
{
    int u=k;
    if(a[i]<b[1])
    {
        return 1;
    }
    //二分法查找满足b[j-1]<=a[i]
    for(int h=1,j=k;h!=j-1;)
    {
        if(b[k=(h+j)/2]<=a[i])
            h=k;
        else
            j=k;
        u=j;
    }
    return u;
}
int LIS()
{
    int v;
    b[1]=a[0];
    for(int i=1,k=1;i<n;i++)
    {
        if(a[i]>b[k])//a[i]>b[k]
        {
            b[++k]=a[i];
            pre[a[i]]=b[k-1];
        }
        else//a[i]≤b[k]
        {
            int m=binary(i,k);
            if(a[i]!=b[m-1])
            {
                pre[a[i]]=b[m-1];
                b[m]=a[i];
            }
        }
        v=k;
    }
    return v;
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    int res=LIS();
    cout<<res<<endl;
    int index=b[res];
    S.push(index);
    while(pre[index]!=0)
    {
        S.push(pre[index]);
        index=pre[index];
    }
    while(!S.empty())
    {
        cout<<S.top()<<' ';
        S.pop();
    }
    return 0;
}
/*
15
7 2 5 6 3 4 9 8 12 13 26 14 30 20 21
*/
/*
7
2 4 3 5 6 4 8
*/
/*
8
12 13 14 1 2 3 2 4
*/

你可能感兴趣的