HDU 5489(2015 Asia Regional Hefei Online F )

【题目链接】
http://acm.hdu.edu.cn/showproblem.php?pid=5489

【解题报告】
题目大意是,在数组A中,删去连续的L个数之后的最长上升子序列(LIS)。

为了解决这道题目,我们先来看LIS的nlogn求法:

1.维护一个单调递增序列LIS

2.对每个点A[i],找到LIS里大于等于它的第一个位置,尝试把它插入到序列里第x个位置。(二分查找)
那么dp[i]表示0..i的序列,以i为末位的最长上升子序列。显然dp[i]=x

3.如果A[i]<LIS[x]那么把LIS[x]更新为A[i]继续向后更新。
4.ans=max{  dp[i] }

这个算法的时间复杂度显而易见是O( nlogn )的。

之后来看这道题目。这时候算法已经很清楚了。
对每个i,我们求把它放在0…i-L这个序列后的LIS,这个维护方法是,每次我们扫到一个a[i],把a[i-L]插入到LIS里。
这时候我们还需要求i..N的LIS,这个很简单,倒过来求一次LIS即可。
最后我们求得每个i的LIS,取最大值即可。
时间复杂度是O( nlogn )。

【参考代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

const int INF=1e9;
const int maxn=1e5+50;

int N,L;
int a[maxn],b[maxn];
int LIS[maxn];
int dp[maxn];

int main()
{
      int T,kase=0;
      cin>>T;
      while(T--)
      {
            scanf("%d%d",&N,&L);
            for( int i=0; i<=maxn; i++ )  LIS[i]=INF;

            memset ( dp,0,sizeof dp );

            for( int i=0; i<N; i++ )
            {
                  scanf( "%d",&a[i] );
                  b[i]=-a[i];
            }

           for( int i=N-1; i>=0; i--  )
           {
                 int pos=lower_bound(   LIS, LIS+N, b[i] )-LIS;
                 if(  b[i]<LIS[pos] )LIS[pos]=b[i];
                 dp[i]=pos+1;
           }
           for( int i=0; i<=maxn; i++ )  LIS[i]=INF;

            int ans=0;
           for(  int i=L; i<N; i++ )
           {
                  int pos=lower_bound(  LIS, LIS+N, a[i] )-LIS;
                  ans=max(   ans, dp[i]+pos );
                  pos=lower_bound(  LIS, LIS+N, a[i-L] )-LIS;
                  LIS[pos]=a[i-L];
                  if(  i==N-1 )   //删去最后的L元素
                  {
                        int pos=lower_bound( LIS, LIS+N,  a[i-L] )-LIS;
                        ans=max(  ans,   pos+1 );
                  }
           }
           printf(   "Case #%d: %d\n",++kase, ans  );
      }

      return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章