hdu4604 不错的子序列问题

题意:
      给你一个栈,里面有n个数,和一个双头队列(空的),每次从栈里拿出一个数据,有三种选择,可以选择丢弃这个数字,也可以放到队头或者队尾,最后问你这个队列你面的最长连续非下降序列的长度...

思路:

      我感觉就是在1025的基础上加强了几个等级,那个题目就是二分贪心求公共子序列,这个题目的细节是用到了1025的思路的,首先我们枚举每一个数,把他当做进队列的第一个数字,那么此时的答案就是 该起点开始的最长费递减子序列 + 该起点开始的最长非递增子序列 - 他们相同的部分.他们相同的部分是的数字肯定是 当前这个数字,那么我们只要找到他俩序列中含有的当前这个数的个数中较小的哪一个减去就行了(相同部分一定是小的是大的子集).值得注意的是我们要倒着枚举数据,减少时间复杂度..


如果没看懂的话我就帮忙回一下1025,正好自己也总结下,dp是自己的弱点,有必要总结..1025 说的是给你n个数(n很大) 求上升子序列...那个题目的思想是 二分 + 贪心(个人感觉 ,有的人认为是 二分 + dp, 随意,dp和贪心相同又不同)开一个数组now[i],记录的是第i个位置最小可以是now[i],对于每一个数num[i]每次我们二分找到num[i],在now[i]中的位置,也就是i,如果这个i>当前长度,那么就更新当前长度,其实这个[i]就是以当前这个数为序列终点的序列的长度,4046上面的那个题目用到的就是在now[i]中再次二分找到now[i]的起始位置,更新的时候我们找的是终止位置,根据这个差我们就找到了重复的num[i]的个数,值得注意一点的就是当前的这个now数组中的数并不是什么子序列的数字,只是单纯的记录某个位置的最小,没有别的意义,光靠这个数组无法输出真正的子序列,但是对于当前时刻当前的num[i],now[i]中连续的num[i]的个数是num[i]为终点的子序列的最后几位数,还有没被别的更新,所以根据now[]中连续的num[i]我们算出的连续num[i].的个数是正确的...



#include<stdio.h>
#include<string.h>

#define N 100000 + 500

int upp[N] ,loww[N] ,now[N];
int num[N];
int same[N];

int minn(int x ,int y)
{
   return x < y ? x : y;
}

int main ()
{
   int t ,n ,i;
   scanf("%d" ,&t);
   while(t--)
   {
      scanf("%d" ,&n);
      for(i = 1 ;i <= n ;i ++)
      scanf("%d" ,&num[i]);
      int low ,up ,mid ,len;
      now[1] = num[n] ,len = 1;
      same[n] = 1;
      for(i = n - 1 ;i >= 1 ;i --)
      {
         low = 1 ,up = len;
         int mk = 0;
         while(low <= up)
         {
            mid = (low + up) >> 1;
            if(num[i] <= now[mid])  
            {
               low = mid + 1;
               mk = mid;
            }
            else up = mid - 1;
         }
         mk ++;
         if(mk > len) len ++;
         now[mk] = num[i];
         loww[i] = mk;
         
         low = 1 ,up = len;
         mk = 0;
         while(low <= up)
         {
            mid = (low + up) >> 1;
            if(num[i] < now[mid])
            {
               low = mid + 1;
               mk = mid;
            }
            else up = mid - 1;
         }
         same[i] = loww[i] - mk;
      }
      
      now[1] = num[n];
      len = 1;
      for(i = n - 1 ;i >= 1 ;i --)
      {
         low = 1 ,up = len;
         int mk = 0;
         while(low <= up)
         {
            mid = (low + up) >> 1;
            if(num[i] >= now[mid])
            {
               low = mid + 1;
               mk = mid;
            }
            else up = mid - 1;
         }
         mk++;
         if(mk > len) len ++;
         now[mk] = num[i];
         upp[i] = mk;                  
         low = 1 ,up = len;
         mk = 0;
         while(low <= up)
         {
            mid = (low + up) >> 1;
            if(num[i] > now[mid])
            {
               low = mid + 1;
               mk = mid;
            }
            else up = mid - 1;
         }
         same[i] = minn(same[i] ,upp[i] - mk);
      }
      
      int ans = 0;
      for(i = 1 ;i <= n ;i ++)
      if(ans < loww[i] + upp[i] - same[i])
      ans = loww[i] + upp[i] - same[i];
      printf("%d
" ,ans);
   }
   return 0;
}

原文地址:https://www.cnblogs.com/csnd/p/12063168.html