现在的位置: 首页 > 综合 > 正文

POJ1631 – 谈解最长非降子序列的又一思路…

2013年02月25日 ⁄ 综合 ⁄ 共 1057字 ⁄ 字号 评论关闭

   最长非降子序列是一类经典的DP问题.....最直接的方法就是每次以当前点扫前面不比自己小的点的DP值...找到其中最大的+1赋给自己作为当前点的DP值....从第一个数一直做到最后一个数...时间复杂度是O(n^2)的....一般的情况这种思路都可以应付....但有些地方就是数据量比较大...O(n^2)的算法会超时.....就比如说POJ1631这道题....并且按照这个思路也没什么地方可以优化了...所以就需要一种新的思路....

   新的思路也很好理解....就是用一个数组 h 来 h [ i ] 记录 i 长度的末尾最小值能是多少....DP也就是建立在这个数组上....不断更新这个数组来得到最优解...

   比如说当前扫到一个数...开始根据 h 数组来判断更新....就从当前点前面所能得到的最大长度 P 开始往前扫....扫到 h [ i ] 的值小于当前值了....就break..然后就判断当前值是否比 h[ i + 1] 大或者说在前面还没达到过 i +1 长度...就将 h [ i + 1] 复制为当前点的值..否则就不用做操作...

   为什么这种思路的效率会更高....当然最差情况也会是 O ( n^2 ) 但绝大多数情况会比经典算法快很多....就是因为每次我扫描的是前面出现的最大长度并且中间还能立马break...而朴素算法是每次必须要扫完前面所有的数...首先前面所有的数必然是大于等于前面所能得到的最长非降子序列的长度..并且绝大多数情况当前点前面的最长非降子序列的长度是远远小于前面所有个数的....再有朴素算法只能把前面的都扫了才能确定值...而这种方法则不需要...

Program : 

#include<iostream>
#include<algorithm>
using namespace std;
int t,n,s[40001],h[40001],i,j,m,p;
int main()
{ 
    scanf("%d",&t);
    while (t--)
    {
       scanf("%d",&n); 
       memset(h,0,sizeof(h));
       for (i=1;i<=n;i++)  scanf("%d",&s[i]); 
       m=1; p=1;
       for (i=1;i<=n;i++)     
       {
          for (j=p;j>=0;j--)
          if  (h[j]<s[i]) break;
          if (!h[j+1])
          {
              p++;
              h[p]=s[i];
              m=p;             
          }else
          {
              if (h[j+1]>s[i]) h[j+1]=s[i]; // 更新h[i]..保证长度为i的子序列末尾最小 
          }
       }
       printf("%d\n",m-1);    
    }  
    return 0;   
}

抱歉!评论已关闭.