$bzoj1046-HAOI2007$ 上升子序列 $dp$ 贪心

  • 题面描述
    • 对于一个给定的\(S=\{a_1,a_2,a_3,…,a_n\}\),若有\(P=\{a_{x_1},a_{x_2},a_{x_3},…,a_{x_m}\}\),满足\((x_1 < x_2 < … < x_m)\)\(( a_{x_1} < a_{x_2} < … < a_{x_m})\)。那么就称\(P\)\(S\)的一个上升序列。如果有多个\(P\)满足条件,那么我们想求字典序最小的那个。任务给出S序列,给出若干询问。对于第\(i\)个询问,求出长度为\(L_i\)的上升序列,如有多个,求出字典序最小的那个(即首先\(x_1\)最小,如果不唯一,再看\(x_2\)最小……),如果不存在长度为\(L_i\)的上升序列,则打印Impossible.
  • 输入格式
    • 第一行一个\(N\),表示序列一共有\(N\)个元素第二行\(N\)个数,为\(a_1,a_2,…,a_n\) 第三行一个\(M\),表示询问次数。下面接\(M\)行每行一个数\(L\),表示要询问长度为L的上升序列。\(N\leq 10^4,M\leq 10^3\)
  • 输出格式
    • 对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.
  • 题解
    • 不要被题面坑了,题面中的字典序不是指\(a_i\),而是指下标。
    • 考虑反过来做最长下降子序列,获得每个点相后最长上升子序列长度。
    • 每次询问,按字典序贪心,从前往后扫,可行就加入。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1e4+5;
const int MAXM=1e3+5;
int n,m;
int a[MAXN],f[MAXN];
int b[MAXN],mxl;
int qry(int x){
	int l=1,r=n+1,mid;
	while (l+1<r){
		mid=(l+r)>>1;
		if (b[mid]>x) l=mid;
		else r=mid;
	}
	return l;
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[n]=1; b[1]=a[n];
	for (int i=n-1;i>=1;i--){
		int len=qry(a[i]);
		f[i]=len+1;
		mxl=max(mxl,f[i]);
		if (b[len+1]<a[i]) b[len+1]=a[i];
	}
	scanf("%d",&m);
	for (int i=1;i<=m;i++){
		int l; scanf("%d",&l);
		if (l>mxl){
			printf("Impossible\n");
			continue;
		}
		else {
			int las=0;
			for (int j=1;j<=n&&l>0;j++){
				if (f[j]>=l&&a[j]>las){
					printf("%d ",a[j]);
					l--; las=a[j];
				}
			}
			printf("\n");
		}
	}
	return 0;
}

posted @ 2019-05-01 22:33  paul120090105  阅读(85)  评论(0)    收藏  举报