BZOJ1046 [HAOI2007]上升序列
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
3 4 1 2 3 6
3
6
4
5
Sample Output
Impossible
1 2 3 6
Impossible
1 2 3 6
Impossible
正解:DP+贪心
解题报告:
其实挺水的却花了我半个多小时。
显然先DP做最长上升子序列,然后做贪心。显然从前往后扫,每次扫到第一个发现长度大于要求长度,且当前值>last的就输出,然后长度--,last=当前值。这样贪心应该是正确的。
我WA了两发,因为当长度变成0时应该及时跳出,而不是往后做。
然后我还学了一下nlogn的最长上升子序列的DP,思想也很简单,就是从后往前做,然后维护每种长度当前的开始的结点的值最大值,对于每个i都二分查找出它之后可以接的最大长度。这显然是nlogn的。
n^2 的做法:
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 10011; 21 const int inf = (1<<30); 22 int n,m,maxl; 23 int a[MAXN],f[MAXN]; 24 25 inline int getint() 26 { 27 int w=0,q=0; 28 char c=getchar(); 29 while((c<'0' || c>'9') && c!='-') c=getchar(); 30 if (c=='-') q=1, c=getchar(); 31 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 32 return q ? -w : w; 33 } 34 35 inline void work(){ 36 n=getint(); for(int i=1;i<=n;i++) a[i]=getint(),f[i]=1; 37 int len; 38 for(int i=n-1;i>=1;i--) {//事实上可以nlogn做最长上升子序列,记录每种长度的出现的最大的值,然后二分查找找到能够匹配的最大长度值 39 len=0; 40 for(int j=i+1;j<=n;j++) { 41 if(a[j]>a[i] && f[j]>=len) len=f[j]; 42 } 43 f[i]=len+1; maxl=max(f[i],maxl); 44 } 45 m=getint(); int x,last; 46 for(int o=1;o<=m;o++) {//实际上这里不能直接输出,应该不断往后找可行解 47 if(o>1) printf("\n"); 48 x=getint(); last=-inf; 49 if(maxl<x) { printf("Impossible"); continue; } 50 for(int i=1;i<=n;i++) { 51 if(f[i]>=x && a[i]>last) { 52 if(last!=-inf) printf(" "); 53 last=a[i]; x--; 54 printf("%d",a[i]); 55 if(!x) break;//及时跳出 56 } 57 } 58 } 59 } 60 61 int main() 62 { 63 work(); 64 return 0; 65 }
nlogn的做法:
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 10011; 21 const int inf = (1<<30); 22 int n,m; 23 int a[MAXN],f[MAXN],c[MAXN];//c[i]表示长度为i的开始结点的值的最大值 24 int maxl; 25 26 inline int getint() 27 { 28 int w=0,q=0; 29 char c=getchar(); 30 while((c<'0' || c>'9') && c!='-') c=getchar(); 31 if (c=='-') q=1, c=getchar(); 32 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 33 return q ? -w : w; 34 } 35 36 inline int search(int x){//二分查找 37 int l=0,r=maxl,mid; int pos; 38 while(l<=r) { 39 mid=(l+r)>>1; 40 if(c[mid]>x) pos=mid,l=mid+1; 41 else r=mid-1; 42 } 43 return pos; 44 } 45 46 inline void work(){ 47 n=getint(); for(int i=1;i<=n;i++) a[i]=getint(); 48 int now; c[0]=inf; 49 for(int i=n;i>=1;i--) {//倒着查找 50 now=search(a[i]); f[i]=now+1; 51 c[f[i]]=max(c[f[i]],a[i]); 52 maxl=max(maxl,f[i]); 53 } 54 m=getint(); int last; 55 for(int o=1;o<=m;o++) { 56 if(o!=1) printf("\n"); 57 now=getint(); last=-inf; 58 if(now>maxl) { printf("Impossible"); continue; } 59 for(int i=1;i<=n;i++){ 60 if(f[i]>=now && a[i]>last) { 61 printf("%d",a[i]); 62 if(now!=1) printf(" "); else break;//记得及时退出 63 now--; last=a[i]; 64 } 65 } 66 } 67 } 68 69 int main() 70 { 71 work(); 72 return 0; 73 }
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!