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;
}