zoukankan      html  css  js  c++  java
  • DP 最长上升子序列(LIS)问题

    最长上升子序列问题,也就是Longest increasing subsequence,缩写为LIS。是指在一个序列中求长度最长的一个上升子序列的问题,是动态规划中一个相当经典问题。上升子序列指的是对于任意的i<j都是满足ai<aj的子序列。

      定义dp[i]:=以ai为末尾的最长上升子序列的长度

    以ai结尾的上升子序列是

      (1)只包含ai的子序列

      (2)在满足j<i并且aj<ai的以aj为结尾的上升子序列末尾,追加上ai后得到的子序列

    满足二者之一。这样就能得到如下的递推关系:

      dp[i]=max{1,dp[j]+1 | j<i且aj<ai}

    使用这一递推公式可以在O(n2)的时间内解决这个问题。

    int n;
    int a[maxn];
    int dp[maxn];
    void solve()
    {
        int res=0;
        for(int i=0;i<n;i++)
        {
            res=dp[i];
            for(int j=0;j<i;j++)//每次添加a[i]时,不断对dp[i]更新
                if(a[j]<a[i])
                    dp[i]=max(dp[i],dp[j]+1);//每次a[i]都应加入到最大的dp[j]中,保证局部性最优
            res=max(res,dp[i]);
        }
        cout<<res<<endl;
    }

    下面介绍时间复杂度为O(n*logn)的DP算法。上面的是利用DP求取针对最末位的元素的最长的子序列。如果子序列的长度相同,那么最末位的元素较小的在之后会更有优势,所以我们在反过来用DP针对相同长度情况下最小的末尾的元素进行求解。 (感觉很像贪心算法)

      dp[i]:= 长度为i+1的上升子序列中末尾元素的最小值(不存在的话就是INF)

    最开始全部dp[i]的值都初始化为INF(一个很大的数)。然后从前到后逐个考虑数列的元素,对于每个aj,如果i=0或者dp[i-1]<aj的话,就用dp[i]=min(dp[i],aj)进行更新。最终找出是的dp[i]<INF的最大的i+1就是结果了。这个DP直接实现的话,时间复杂度依然为O(n2)。我们可以对这进一步优化。首先DP数列中除了INF之外是单调递增的,所以可以知道对于每个aj最多只更新一次。对于这次更新的位置,我们不用逐个遍历,用二分搜索,二分查找的时间复杂度是O(logn),所以可在O(nlogn)时间内求出结果。

     假设a = [4, 2, 6, 3, 1, 5], 初始dp=[], 具体算法运行步骤如下:

    1. a[0]=4 => dp=[4];
    2. a[1]=2 => dp=[2];
    3. a[2]=6 => dp=[2, 6];
    4. a[3]=3 => dp=[2, 3];
    5. a[4]=1 => dp=[1, 3];
    6. a[5]=5 => dp=[1, 3, 5];
      所以这个a数组的LIS就是len(dp)=3。 从运行步骤里可以看出,如果一个数很小, 可以作为LIS的头部或者中部, 让后面的数字更容易接到它后面,以此增大LIS长度;而一个数非常大, 则可以很容易接到LIS的尾部, 也一样能增大LIS长度; 所以让它们找准自己的定位还是非常重要的。

      int dp[maxn];
      const int INF=0x3f3f3f3f;
      void solve()
      {
          memset(dp,INF,sizeof(dp));
          for(int i=0;i<n;i++)
              *lower_bound(dp,dp+n,a[i])=a[i];
          cout<<lower_bound(dp,dp+n,INF)-dp<<endl;
      }

     lowee_bound这个STL函数。这个函数从已排好序的数列中利用二分查找找出指向满足ai>=k的ai的最小的指针。类似的函数还有upper_bound,这一函数求出的是指向满足ai>k的ai的最小的指针。

  • 相关阅读:
    JS中的事件&对象
    WSL使用小结:从ArchLinux到Manjaro
    WSL学习:安装ArchLinux和Root/Cling以及注意事项
    Qt5网络请求使用及WebRequest函数
    有道词典中的OCR功能:第三方库的变化
    C++学习(二):学会使用stringstream
    C语言集锦(三)Direct3D和GDI+的例子
    网络那些事:浏览器、网络协议以及常见软件
    C++学习(一):现代C++尝试
    PostScript学习:另一种缩写为PS的技术
  • 原文地址:https://www.cnblogs.com/orion7/p/7516035.html
Copyright © 2011-2022 走看看