最长上升子序列LIS算法粗略讲解

定义

LIS(Longest Increasing Subsequence)最长上升(不下降)子序列,有两种算法复杂度为O(n*logn)和O(n^2)。在上述算法中,若使用朴素的顺序查找在D1..Dlen查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来算法相比没有任何进步。但是由于D的特点(2),在D中查找时,可以使用二分查找高效地完成,则整个算法时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D在算法结束后记录的并不是一个符合题意的最长上升子序列!算法还可以扩展到整个最长子序列系列问题。

问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <cmath>
#include <queue>
using namespace std;
const int maxn=1e3+10;
int n,a[maxn],dp[maxn];
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",a+i);
            dp[i]=1;
        }
          for(int i=1;i<n;i++)
          {
              for(int j=0;j<i;j++)
              {
                  if(a[i]>a[j])
                  {
                      dp[i]=max(dp[i],dp[j]+1);
                  }
              }
          }
          int maxi=0;
          for(int i=0;i<n;i++)
          {
           printf("sss  i=%d   dp[i]=%d\n",i,dp[i]);
              maxi=max(maxi,dp[i]);
          }
          printf("%d\n",maxi);
    }
    return 0;
}

(nlogn):维护一个一维数组c,并且这个数组是动态扩展的,初始大小为1,c[i]表示最长上升子序列长度是i的所有子串中末尾最小的那个数,根据这个数字,我们可以比较知道,只要当前考察的这个数比c[i]大,那么当前这个数一定能通过c[i]构成一个长度为i+1的上升子序列。当然我们希望在C数组中找一个尽量靠后的数字,这样我们得到的上升子串的长度最长,查找的时候使用二分搜索,这样时间复杂度便下降了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <cmath>
#include <queue>
using namespace std;
int n,a[40005],dp[40005],b[40005];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            dp[i]=1;
        }
        int l,r,mid,maxi=0,len=0;
        for(int i=1;i<=n;i++)
        {
            int num=a[i];
            l=1;
            r=len;
            while(l<=r)
            {
              mid=(l+r)>>1;
              if(num>b[mid])
                 l=mid+1;
              else
                 r=mid-1;
            }
            b[l]=num;
            dp[i]=l;
            if(l>len)
                len=l;
            if(dp[i]>maxi)
                maxi=dp[i];
        }
            printf("%d\n",maxi);
    }
    return 0;
}

END!!!!!!!!!!!!!!!!!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值