zoukankan      html  css  js  c++  java
  • HAOI 2007 上升序列

      对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1<x2<…<xm)且(ax1<ax2<…<axm)。那么就称PS的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。

    任务

        给出S序列,给出若干询问。对于第i个询问,求出长度为Li的上升序列,如有多个,求出字典序最小的那个(即首先x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印Impossible.

    输入

        第一行一个N,表示序列一共有N个元素

        第二行N个数,为a1,a2,…,an

        第三行一个M,表示询问次数。下面接M行每行一个数Li,表示要询问长度为Li的上升序列。

    输出

        对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.

    样例

    lis.in

    6

    3 4 1 2 3 6

    3

    6

    4

    5

    lis.out

    Impossible

    1 2 3 6

    Impossible

    数据范围

    N<=10000

    M<=1000

                              【算法简析】

        首先很容易发现,如果Li的长度大于这个序列的LIS的长度,那么一定无解,否则一定有解。为方便讨论,下文只考虑有解情况。

    暂且抛开“字典序最小”这个限制条件,即任何长度为Li的上升序列Pi都可以看作符合要求,那么题目的所有要求都可以由LIS来满足。这样我们就可以直接求这个序列的LIS,然后对于所有m次询问,用O(n)的时间输出,这样算法总的时间复杂度就是O(nlogn+nm)

    那么如果要求字典序最小又如何处理呢?其实我们完全可以在相同的时间复杂度内解决问题,只是需要一些转化。

    注意到在求LIS的过程中,实际上我们得到的不只是LIS,而是每个数作为子序列末尾的所有最长上升序列。把问题换一个角度,假设我们知道以每个数a[i]作为开头的最长上升序列的长度lis[i],那么就很容易利用贪心思想构造出答案了。这个贪心成立的前提就是:只要以该元素为开头的LIS的长度大于等于Li,则一定有解。该结论是很显然的。

    下面简述贪心的方法:

    累加下标j,从左到右扫描序列,如果lis[j]Li,则答案序列的第Li个数就是a[j],并且Li减1。重复上述过程直到Li=0。最后将答案序列倒着输出即可。

    这个贪心显然是成立的。由于j是从小到大逐次增加,所以第一个符合lis[j]Li的必然就是最优解。每次找到一个符合的元素,所需要的LIS的长度就会减少1。这样重复下去得到的必然是最优解。

    由于只需要对序列做一遍扫描,时间复杂度最坏O(n)。构造答案的时间为O(nm),加上预处理求LISO(nlogn)的时间,总时间复杂度为O(nlogn+nm)

     

    细节:求以a[i]为开头的LIS比较麻烦,可以把序列反过来后求以a[i]为末尾的LDSLongest Decreasing Subsequence),这样处理起来较方便。

     1 #include<bits/stdc++.h>
     2 const int MAXN=100010;
     3 using namespace std;
     4 int n,m,cnt,a[MAXN],f[MAXN],best[MAXN];
     5 int BinarySearch(int x){
     6     int lowerBound=1,upperBound=cnt,ans=0;
     7     while(lowerBound<=upperBound){
     8         int mid=(lowerBound+upperBound)/2;
     9         if(best[mid]>x){
    10               ans=mid;
    11               lowerBound=mid+1;
    12         }
    13         else upperBound=mid-1;
    14       }
    15       return ans;
    16 }
    17 
    18 void solve(int x){
    19     int last=0;
    20     for(int i=1;i<=n;i++)
    21     if(f[i]>=x&&a[i]>last){
    22       printf("%d",a[i]);
    23       if(x!=1) printf(" ");
    24       last=a[i];
    25       x--;
    26       if(!x) break;
    27     }
    28       printf("
    ");
    29 }
    30 
    31 void preDP(){
    32     for(int i=n;i>=1;i--){
    33         int t=BinarySearch(a[i]);
    34         f[i]=t+1;
    35         cnt=max(cnt,t+1);
    36         if(best[t+1]<a[i])
    37           best[t+1]=a[i];
    38       }
    39 }
    40 
    41 int main(){
    42     scanf("%d",&n);
    43     for(int i=1;i<=n;i++)
    44         scanf("%d",&a[i]);
    45     preDP();
    46     scanf("%d",&m);
    47     for(int i=1;i<=m;i++){
    48         int len;
    49         scanf("%d",&len);
    50         if(len<=cnt)
    51           solve(len);
    52         else puts("Impossible");
    53   }
    54   return 0;
    55 }
  • 相关阅读:
    Asp.Net细节性问题精萃(转)
    开发OFFICE插件总结(转)
    校内网开心网数据同步引发的讨论(转)
    C++指针探讨 (三) 成员函数指针 (转)
    C++指针探讨 (二) 函数指针 (转)
    【原创】编程获取PE文件信息的方法(转)
    为.net开发者提供的一份关于存储过程的评论(转)
    C++指针探讨 (一)数据指针 (转)
    如何批量修改PPT字体、大小、颜色(转)
    搜索引擎里的爱人(转)
  • 原文地址:https://www.cnblogs.com/CXCXCXC/p/4684734.html
Copyright © 2011-2022 走看看