首页 > 代码库 > 动态规划-最长非降子序列

动态规划-最长非降子序列

有关概念:

  最长上升子序列(LIS,Longest Increasing Subsequence),在一个序列中最长的单调递增的子序列

思路:
  求LIS通常有O(n2)和O(nlogn)两种算法

(1)O(n2)算法

  fi表示以第i个数结尾的LIS长度

  对于序列中的一个数i,在i前面枚举数j,j满足比i小且fj最大,将i作为j的后继

  伪代码&状态转移方程:

  f1=1

  for i=2...n

    for j=1...i-1

      if(aj<ai)fi=max(fi,fj+1)

  最后结果在f1~fn中取最大值

  当然,可以在更新fi的时候顺便记录j的位置,在最后可以输出整个LIS

 1 #include<cstdio>
 2 #define MAXN 
 3 int n,a[MAXN],f[MAXN],ans;
 4 inline int max(int x,int y)
 5 {
 6     return x>y?x:y;
 7 }
 8 int main()
 9 {
10     scanf("%d",&n);
11     for(int i=1;i<=n;++i)
12     {
13         scanf("%d",&a[i]);
14         f[i]=1;
15     }
16     for(int i=2;i<=n;++i)
17         for(int j=1;j<i;++j)
18             if(a[i]>=a[j]&&f[j]+1>=f[i])f[i]=f[j]+1;
19     for(int i=1;i<=n;++i)ans=max(ans,f[i]);
20     printf("%d",ans);
21     return 0;
22 }

(2)O(nlogn)算法

  fi表示长度为i的非降子序列的最小末尾,len记录当前最长的非降子序列长度

  可得f1<=f2<=f3<=...<=fmaxlen

  每次读入一个数字k,先与flen比较,如果大于则添加至f++len,作为原来flen的后继

  否则在f中找到fi<k<=fi+1,更新为fi+1为k,即k比原来fi+1更优,可作为fi的后继

  最后输出len即可

  然而查找过程可以用二分优化,就得出这种算法

  (样例推导见大犇博客http://www.cnblogs.com/ziyi--caolu/p/3227121.html)

 1 #include<cstdio>
 2 #define MAXN 
 3 int n,s[MAXN],f[MAXN],len;
 4 int main()
 5 {
 6     scanf("%d",&n);
 7     scanf("%d",&s[1]);
 8     f[1]=s[1];
 9     for(int i=2;i<=n;++i)
10     {
11         scanf("%d",&s[i]);
12         if(s[i]>f[len])f[++len]=s[i];
13         else
14         {
15             int l=1,r=len;
16             while(l<r)
17             {
18                 int mid=(l+r)>>1;
19                 if(s[i]>f[mid])l=mid+1;
20                 else r=mid;
21             }
22             f[l]=s[i];
23         }
24     }
25     printf("%d",len);
26     return 0;
27 }

 

动态规划-最长非降子序列