bzoj1046: [HAOI2007]上升序列

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的上升序列。
Output
对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.
Sample Input
6
3 4 1 2 3 6
3
6
4
5
Sample Output
Impossible
1 2 3 6
Impossible
HINT
数据范围
N<=10000
M<=1000

【解析】
本道题是最长上升子序列再加上一点小改动
那么我们就先来看在序列中找长度为L的方法..
我们在处理序列时可以找到一个最长的上升子序列,长度记为maxx
那么假如要求的L比maxx还要大,那肯定不存在,输出‘Impossible’
那假如不呢?
for ( i = 1; i <= n; i ++ ){
	if ( f[i] >= k && a[i] > p ){
		printf ( "%d", a[i] );
		k --; p = a[i];
		if ( k == 0 ){ printf ( "\n" ); break; }
		else printf ( " " );
	}
}
这是最核心的代码
首先我们需要一个数p,代表当前提取数列中的最后一个,那么也就是说下一个要找的数一定要比p大
假如已经满足了这个数比p大了,这时候我们做最长上升子序列的f数组就起到一个至关重要的作用了
因为我的f数组中fi代表的是从i开始往后的最长上升的长度,只要fi比当前所需要的k大的话就可以选择当前的数了
接着k--代表所需的数个数减少一个,更新p使p等于ai
【完整代码】
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int f[11000], a[11000];
int n, m;
int _max ( int x, int y ){ return x > y ? x : y; }
int main (){
	freopen ( "a.in", "r", stdin );
	freopen ( "a.out", "w", stdout );
	int i, j, k, maxx; maxx = 0;
	scanf ( "%d", &n );
	memset ( last, -1, sizeof (last) );
	for ( i = 1; i <= n; i ++ ) scanf ( "%d", &a[i] );
	for ( i = n; i >= 1; i -- ){
		f[i] = 1;
		for ( j = i+1; j <= n; j ++ ){
			if ( a[j] > a[i] && f[j]+1 > f[i] ) f[i] = f[j] + 1;
		}
		maxx = _max ( f[i], maxx );
	}
	scanf ( "%d", &m );
	for ( int mi = 1; mi <= m; mi ++ ){
		scanf ( "%d", &k );
		if ( k > maxx ){ printf ( "Impossible\n" ); continue; }
		int p = -0x7fffffff;
		for ( i = 1; i <= n; i ++ ){
			if ( f[i] >= k && a[i] > p ){
				printf ( "%d", a[i] );
				k --; p = a[i];
				if ( k == 0 ){ printf ( "\n" ); break; }
				else printf ( " " );
			}
		}
	}
	return 0;
}


你可能感兴趣的:(bzoj1046: [HAOI2007]上升序列)