zoukankan      html  css  js  c++  java
  • 最长递增子序列长度LIS

    最长递增子序列Longest Increasing Subsequence (LIS) 指一个序列中所包含的最长的递增子序列。
     例如, 序列{2, 1, 4, 2, 3, 7, 4, 6}的LIS是{1, 2, 3, 4, 6},其长度为5。写一函数计算一个序列中的最长子序列长度。

    一般思路:使用动态规划,数组记为a[],用f[i]表示从第0个元素到第i个元素的LIS长度(初始化为1,因为a[i]本身长度为1),对于每一个i,需要和i以前的元素进行比较,并更新f[i]的值,即比较a[i]和a[j],0 <= j < i。

    状态转移方程为f[i] = max{f[i], f[j]+1, if a[i] > a[j]}, 0 <= j < i。 用result记录最长的LIS长度。时间复杂度为O(N^2)。

    code:

    View Code
     1 //最长递增子序列长度Version1, DP
     2 int LongestIncreasingSubsequenceV1(int *a, int N)
     3 {
     4     if (NULL == a || N <=0)
     5     {
     6         return -1;
     7     }
     8     int *f = new int[N];
     9     //初始化f[i]
    10     for (int i = 0; i < N; i++)
    11     {
    12         f[i] = 1;
    13     }
    14     
    15     int result = f[0];
    16 
    17     for (int i = 1; i < N; i++)
    18     {
    19         for (int j = 0; j < i; j++)
    20         {
    21             if (a[i] > a[j] && f[j] + 1 > f[i])
    22             {
    23                 f[i] = f[j] + 1;
    24             }
    25         }
    26         if (result < f[i])
    27         {
    28             result = f[i];
    29         }
    30     }
    31     delete f;
    32     f = NULL;
    33     return result;
    34 }

    改进:上面比较a[i]和a[j]是通过遍历来比较的,有没更好的方法?

    假如使用B[len]来记录长度为len的LIS的最小末尾,那么就可以直接比较a[i]和B[len],找到a[i]在B中合适的位置。

    如序列a[8]={2, 1, 4, 2, 3, 7, 4, 6},初始B[0] = (signed int)0x80000000,B[1] = a[0] =2;

    对于a[1],a[1]<B[1],更新B[1] = 1;

    对于a[2],a[2]>B[1],故B[2] = 4;

    对于a[3],B[1]<a[3]<B[2],更新B[2] = 2;

    对于a[4] ,a[4]>B[2],故B[3] = 3;

    对于a[5] ,a[5]>B[3],故B[4] = 7;

    对于a[6],B[3]<a[6]<B[4],故更新B[4]=4;

    对于a[7],a[7]>B[4],故B[5]=6.至此过程结束。

    从上面过程可以发现,对于长度为i的最小末尾数组B[i],有以下性质,当i<j时, B[i]<B[j]。在有序数组中查找,可以使用二分查找提高查找效率。

    设当前总长度为k时,比较a[i]和B[k]

    1.若a[i]>B[k],则B[k+1] = a[i]

    2.若a[i]<B[k], 二分查找B中位置j使得,B[j]<a[i]<B[j+1],并更新B[j+1] = a[i];

    这两种情况可以合并在一起。

    code:

    View Code
     1 //最长递增子序列长度Version2, 改进的DP
     2 int LongestIncreasingSubsequenceV2(int *a, int N)
     3 {
     4     if (NULL == a || N <=0)
     5     {
     6         return -1;
     7     }
     8     
     9     int *B = new int[N + 1];
    10     B[0] = (signed int)0x80000000;
    11     B[1] = a[0];
    12     int len = 1;
    13     int low;
    14     int high;
    15     int mid;
    16     for (int i = 1; i < N; i++)
    17     {
    18         low = 0;
    19         high = len;
    20         while (low <= high)
    21         {
    22             mid = low + (high - low) / 2;
    23             if (a[i] > B[mid])
    24             {
    25                 low = mid + 1;
    26             }
    27             else
    28             {
    29                 high = mid - 1;
    30             }
    31         }
    32         B[low] = a[i];
    33         if (low > len)
    34         {
    35             len++;
    36         }
    37     }
    38     delete B;
    39     B = NULL;
    40     return len;
    41 }
  • 相关阅读:
    《校园封神榜》第二阶段个人工作总结——第五天
    寻找水王2——寻找三个小水王
    站立会议04(第二次冲刺)
    站立会议03(第二次冲刺)
    站立会议02(第二次冲刺)
    站立会议01(第二次冲刺)
    测试计划
    评价cnblogs.com的用户体验
    第一次冲刺各组评价的回复
    第一次冲刺对各组的评价
  • 原文地址:https://www.cnblogs.com/ivorfeng/p/2988433.html
Copyright © 2011-2022 走看看