Description
对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax
2 < … < 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
首先求出以每个数为开头上升序列长度,即倒着做最长下降子序列
然后,把字典序尽量小的放前面
即若要求的序列长度为x,如果以第一个数(字典序最小的数)开头的最长上升子序列大等于x,则将它放在答案第一个,第二个数开头小于x,则舍弃,第三个大于x-1,放答案第二个,以此类推
其中倒着做最长下降子序列的时候可以直接暴力做,也可以二分。二分可以看hzwer神牛的博客。
暴力做代码如下:
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = a; i <= b; i++)
const int N = 10005;
int a[N], dp[N], lis, n, m;
int main(){
scanf("%d", &n);
rep(i, 1, n){
scanf("%d", &a[i]);
}
for(int i = n; i >= 1; i--){
for(int j = n; j >= 1; j--){
if(a[j] > a[i]){
dp[i] = max(dp[i], dp[j]);
}
}
dp[i]++;
lis = max(lis, dp[i]);
}
scanf("%d", &m);
while(m--){
int x;
scanf("%d", &x);
if(x > lis){
printf("Impossible\n");
}
else{
int cnt = 0;
rep(i, 1, n){
if(dp[i] >= x && a[i] > cnt){
printf("%d",a[i]);
if(x != 1) printf(" ");
cnt = a[i];
x--;
if(x == 0) break;
}
}
printf("\n");
}
}
return 0;
}