最长上升子序列的nlogn算法实现(用栈)

最长上升子序列的nlogn算法实现(用栈)

大体算法思想是设置一个栈,数据结构里严格意义上的栈是后进先出,但是这里的栈中间有稍微不一样的地方在于中间的元素也会被覆盖掉,算法过程是,第一个元素入栈,以后没读取一个元素t,如果t比栈顶元素的大,那么就入栈。如果比它小,那么就用二分搜索的方法在栈里面找到这样一个元素stack[i],使得stack[i]>t并且stack[i-1](如果有的话)<t,然后就用t去更新这个stack[i]元素。举个例子,如果输入一下数据

5 9 4 1 3 7 6 7

那么栈的变化过程为:

5
5 9
4 9 //用4更新了5
1 9
1 3
1 3 7
1 3 6
1 3 6 7

最后的结果,最长递增子序列的长度就是栈的大小,这里是4。要注意的是最后栈里的元素并不就一定是所求的序列,例如如果输入

2 5 1

那么最后得到的栈应该是

1 5

而实际上要求的序列是

2 5

至于要怎么求这个序列。。。这个。。。看了讲解之后自己还是没研究透彻。。。哈哈,有空再说了。算法的正确性没有经过严格的证明,不过想想看应该是对的吧,哈哈,偷懒了。

代码(写得有些丑陋的):

/*
习题 33:最长递增子序列★★★

问题描述:
所谓子序列,就是在原序列里删掉若干个元素后剩下的序列,以字符串"abcdefg"为例子,去掉bde得到子序列"acfg"
现在的问题是,给你一个数字序列,你要求出它最长的单调递增子序列。

输入:
多组测试数据,每组测试数据第一行是n(1<=n<=10000),下一行是n个比1e9小的非负整数

输出:
对于每组测试数据输出一行,每行内容是最长的单调递增子序列的长度

样例输入:
5
1 2 4 8 16
5
1 10 4 9 7
9
0 0 0 1 1 1 5 5 5

样例输出:
5
3
3

难度:normal
*/
#include <cstdio>

#define MAXN 10000
long long stack[MAXN];
int n;
long long t;
int top;

int main()
{
    int i;
    int mid,high,low;
    while(scanf("%d",&n)==1 && n!=0)
    {
        scanf("%lld",&stack[0]);
        top=1;
        for(i=1;i<n;i++)
        {
            scanf("%lld",&t);
            if(t>stack[top-1])                             //一
                stack[top++]=t;
            if(t==stack[top-1])                          //二
                continue;
            else                                               //三
            {
                low=0;
                high=top-1;
                while(true)
                {
                    mid=(low+high)/2;
                    if(stack[mid]>=t && (mid==0 || stack[mid-1]<t))
                    {
                        stack[mid]=t;
                        break;
                    }
                    if(stack[mid]<t)
                        low=mid+1;
                    else
                        high=mid-1;
                }
            }
        }
        printf("%d\n",top);
    }
    return 0;
}


你可能感兴趣的:(最长上升子序列的nlogn算法实现(用栈))