Stock Exchange(LIS优化)

Stock Exchange

题目描述

The world financial crisis is quite a subject. Some people are more relaxed while others are quite anxious. John is one of them. He is very concerned about the evolution of the stock exchange. He follows stock prices every day looking for rising trends. Given a sequence of numbers p1, p2,…,pn representing stock prices, a rising trend is a subsequence pi1 < pi2 < … < pik, with i1 < i2 < … < ik. John’s problem is to find very quickly the longest rising trend.

Input
Each data set in the file stands for a particular set of stock prices. A data set starts with the length L (L ≤ 100000) of the sequence of numbers, followed by the numbers (a number fits a long integer).
White spaces can occur freely in the input. The input data are correct and terminate with an end of file.

Output
The program prints the length of the longest rising trend.
For each set of data the program prints the result to the standard output from the beginning of a line.

Sample Input
6
5 2 1 4 5 3
3
1 1 1
4
4 3 2 1

Sample Output
3
1
1

题解:求最长上升子序列(LIS),由于数据范围比较大,普通的求最长上升子序列的方法时间复杂度为o(n^2),这里明显会超时,所以需要用时间复杂度为o(nlogn)的方法来优化。

下面是思路:

新建一个 low 数组,low [ i ]表示长度为i的LIS结尾元素的最小值。对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。因此,我们只需要维护 low 数组,对于每一个a[ i ],如果a[ i ] > low [当前最长的LIS长度],就把 a [ i ]接到当前最长的LIS后面,即low [++当前最长的LIS长度] = a [ i ]。
那么,怎么维护 low 数组呢?
对于每一个a [ i ],如果a [ i ]能接到 LIS 后面,就接上去;否则,就用 a [ i ] 取更新 low 数组。具体方法是,在low数组中找到第一个大于等于a [ i ]的元素low [ j ],用a [ i ]去更新 low [ j ]。如果从头到尾扫一遍 low 数组的话,时间复杂度仍是O(n^2)。我们注意到 low 数组内部一定是单调不降的,所有我们可以二分 low 数组,找出第一个大于等于a[ i ]的元素。二分一次 low 数组的时间复杂度的O(lgn),所以总的时间复杂度是O(nlogn)。

AC代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <map>
using namespace std;

int a[100010],low[100010];

int check(int len,int x)
{
    int l=1,r=len,mid=0,index;
    while(l<=r)
    {
        mid=l+(r-l)/2;
        if(low[mid]>=x)
        {
            index=mid;
            r=mid-1;
        }
        else
            l=mid+1;
    }
    return index;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        int len=0;
        low[++len]=a[1];
        for(int i=2;i<=n;i++)
        {
            if(a[i]>low[len])
            low[++len]=a[i];
            else
                low[check(len,a[i])]=a[i];
        }
        cout<<len<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值