zoukankan      html  css  js  c++  java
  • 最长单调递增子序列

    1.问题描述:

    求一个正整数序列的最长单调自增子序列,子序列不要求是连续的。例如

    Input:5

    5 2 4 3 1

    Output:2

    2. 算法复杂度是O(N*N)

    f[i]是以a[i]为最大值的子序列,那么f[]的最大值就是要的结果。

    int f[],a[];

    f[0] = 1;

    for(i = 1 ; i < n ; i++ )

    {

      f[i] = 1;

      for(j = 0 ; j < i ; j++)

      {

        If(a[j] < a[i] && f[j]+1 > f[i])//等号有没有要视题目而定

        {

          f[i] = f[j] +1;

        }

      }

    }

    很显然实践复杂度是O(N*N),那么有没有更快的算法呢?按照正常的思路更快的复杂度应该就是O(N*logN),那么就要涉及到二分了。

    3. 算法复杂度是O(N*logN)

    可是话又说回来,那个logN到底怎么实现的呢?上网搜了搜说的都有点抽象,捉摸了一下,是这个样子滴!建立一个辅助数组c[n],c[i]存储的是子序列长度为i的序列最后一个值(实际上子序列长度为i的子序列有多个,要的是子序列最后一个值最小的。后面解释后什么!!!)。这时要遍历要处理的数组a[n],for(i=1;i<n;i++)//从第二值开始,因为第一个值用来初始化了

    {

      j=find(c,n+1,a[i]);//find是一个二分查找

      c[j]=a[i];

      b[i]=j;

    }

    请看一下上面的例子实际执行的情况:C数组变化的情况

    -1 5

    -1 2

    -1 2 4

    -1 1 2

    -1 1 4

    -1 1 3

    A数组遍历是从前往后的,处理a[i-1]a[i]以及后面的值肯定还没有处理,前面的值都处理过了,看c数组,每个a数组中的值和c数组中值进行比较,找到合适的位置插入(若插入到c数组的末尾,那么就属于最长递增子序列长度加1,实际上c数组的长度就是最后的最长单调递增子序列的长度。),否则这就替换掉了c数组中原来位置存储的值,这种替换时有意义的,主要是为了后来的a数组中的值计算b用(b[i]中保存的是以a[i]为最后一个元素的最长单调递增子序列。)好处是若a[i] <a[j],b[i]=b[j],那么在c中肯定要保存a[i]呀!!(注意c数组的下标代表的是子序列的长度,c数组中的值也是按递增顺序排列的。这才可能用二分呢,亲)。和O(N*N)的主要区别就是巧妙的借用了c数组,本题的关键就是理解c数组的意义。可以手动模拟一下算法执行的步骤,重要模拟cb数组的变化情况。下面给出完整的算法。

    #include <iostream>

    using namespace std;

    int find(int *a,int len,int n)//二分find

    {

      int left=0,right=len,mid=(left+right)/2;

      while(left<=right)

      {

        if(n>a[mid]) left=mid+1;

        else if(n<a[mid]) right=mid-1;

        else return mid;

        mid=(left+right)/2;

      }

      return left;

    }

    void fill(int *a,int n)

    {

      for(int i=0;i<=n;i++)

        a[i]=1000;//这就是一个初始化,无所谓!!

    }

    int main()

    {

      int max,i,j,n,a[100],b[100],c[100];

      while(cin>>n)

      {

        fill(c,n+1);

        for(i=0;i<n;i++)

          cin>>a[i];

        c[0]=-1;//要懂得用这种天然的最小值

        c[1]=a[0];//初始化

        b[0]=1;//b[i]表示以a[i]结尾的最长单调递增子序列

        for(i=1;i<n;i++)//复杂度是N

        {

          j=find(c,n+1,a[i]);//复杂度是logN

          c[j]=a[i];

          b[i]=j;

        }

         for(max=i=0;i<n;i++)//遍历一遍找到最大值

          if(b[i]>max)

            max=b[i];

        cout<<max<<endl;

      }

      return 0;

    }

  • 相关阅读:
    Android开发之适配器-ListView适配器的重复数据
    Android开发之TextView的下划线添加
    Android 自定义View修炼-Android开发之自定义View开发及实例详解
    Android 开发之自定义Dialog及UI的实现
    Android开发之ContentProvider(内容提供者)
    XC文件管理器(Android应用)
    高效 告别996,开启java高效编程之门 4-1普通码农与风骚码农资源关闭PK
    高效 告别996,开启java高效编程之门 4-2垃圾回收与物理资源释放
    高效 告别996,开启java高效编程之门 3-29实战案例五:排序
    高效 告别996,开启java高效编程之门 3-28实战案例四:分组
  • 原文地址:https://www.cnblogs.com/BeyondAnyTime/p/2508415.html
Copyright © 2011-2022 走看看