LIS——最长不下降子序列

输入一个n,以及n个数据,输出最长的不下降的子序列

大意:

有两种思路:

1.O(n^2)想法

用一个num来记录以该数字为末数字的长度

用一个if限制两个条件,后面小于前面并且后面的长度大于前面的长度+1那么说明需要更新了,最后只要遍历所有的数字在num中看以那个数字为开始点的长度最长。这个比较好理解

#include<cstdio>
const int MAX = 150;
int a[MAX],num[MAX];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1; i<= n ;i++)
        scanf("%d",&a[i]);
    for(int i = 1; i <= n ; i++){
        num[i] = 1;
        for(int j = 1; j < i ; j++){
            if(a[i] > a[j] &&num[i] < num[j] + 1)
            num[i] = num[j]+1;
        }
    }
     int max = 0;
     for(int i = 1; i <= n ; i++){
            if(max < num[i])
            max = num[i];
     }
     printf("%d",max);
     return 0;
}
View Code

2.O(n*logn)想法

这个比较难以理解

用b[k]来记录以该下标为长度,且最后一个数为它的数值

b[1] = a[1],然后循环,判断是否a[i] > b[k],如果是的话,b长度就变长,值就变成a[i],否则的话从长度为1开始找一直找到以长度为k的,因为b是单调递增的,所以在其中找大于b[j],小于b[j+1]这个值的下标,就相当于在一个单调的串中找一个与要找的n相同的值,用二分法,不过以下标为二分的话,l = mid + 1,r = mid -1 ,返回l的值,左闭右开。

#include<cstdio>
using namespace std;
const int MAX = 1010;
int a[MAX],b[MAX];
int main()
{
    int n,k,l,r,mid;
    scanf("%d",&n);
    for(int i = 1; i <= n ; i++)
        scanf("%d", &a[i]);
    b[1] = a[1];
    int i;
    for(i = 2, k = 1;i <= n ; i++){
        if(a[i] > b[k]) b[++k] = a[i];//b[k]存储长度为k的最后一个数字
        else {
             l = 1, r = k;
            while(l<= r){//二分查找
               mid = (l + r)/2;
              if(b[mid] < a[i]) l = mid + 1 ;
              else if(b[mid] > a[i]) r = mid - 1;
              else break;
            }
            b[l] = a[i];
        }
    }
    printf("%d\n",k);
    return 0;
}
View Code

 也可以调用库函数 lower_bound  二分函数

lower_bound(a, a + n, k) 用二分找a[i]大于k的最小的指针

int dp[maxn];
void solve()
{
    fill(dp, dp + n ,inf);
    for(int i = 0 ; i < n ; i++)
        *low_bound(dp, dp + n ,a[i]) = a[i];
    printf("%d\n",lower_bound(dp,dp+n,inf)-dp);
}

  

 

posted @ 2015-05-13 19:39  Painting、时光  阅读(244)  评论(0编辑  收藏  举报