BZOJ 1046: [HAOI2007]上升序列 【最长升降】

1046: [HAOI2007]上升序列

Time Limit: 10 Sec Memory Limit: 162 MB

Description

  对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax2 < … < axm)。那么就称P为S的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。任务给出S序列,给出若干询问。对于第i个询问,求出长度为Li的上升序列,如有多个,求出字典序最小的那个(即首先x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印Impossible.

Input

  第一行一个N,表示序列一共有N个元素第二行N个数,为a1,a2,…,an 第三行一个M,表示询问次数。下面接M行每行一个数L,表示要询问长度为L的上升序列。N<=10000,M<=1000

Output

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

Sample Input

6
3 4 1 2 3 6
3
6
4
5

Sample Output

Impossible
1 2 3 6
Impossible

题解

这题是典型的最长升,首先我们知道,我们选的这个序列的最长升肯定要大于等于L,否则选择没有意义。
然后如果要选择字典序最小的(PS:下标的字典序最小,我就被坑了),所以我们选择月前越好,所以,当前枚举到i,之前选择了K个,如果当前的最长升大于等于L-K,且a[i]>lst(上一次)选的,那么就选择i,然后更新lst,直到K==L就停。

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,a[10005],f[10005],ans;
bool cmp(int x,int y){return a[x]<a[y]||(a[x]==a[y]||x<y);}
int main(){
//  freopen("prob.in","r",stdin);
//  freopen("prob.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    a[0]=-(1e9);a[n+1]=1e9;
    for(int i=n;i;i--)
    for(int j=i+1;j<=n+1;j++)
    if(a[i]<a[j]&&f[i]<f[j]+1) f[i]=f[j]+1;
    scanf("%d",&m);
    for(int j=1;j<=m;j++){
        int len,lst=0;scanf("%d",&len);ans=0;
        for(int i=1;i<=n;i++)
        if(f[i]>=len-ans&&a[i]>lst){
            lst=a[i],ans++,printf(ans==len?"%d\n":"%d ",a[i]);
            if(ans==len) break;
        }
        if(lst==0) printf("Impossible\n");
    }
    return 0;
}
posted @ 2018-03-13 18:43  XSamsara  阅读(127)  评论(0编辑  收藏  举报