【BZOJ】【1046】【HAOI2007】上升序列

DP+贪心


  啊……其实是个水题,想的复杂了

  令f[i]表示以 i 为起始位置的最长上升子序列的长度,那么对于一个询问x,我们可以贪心地从前往后扫,如果f[i]>=x && a[i]>last,则x--,last=a[i]

  保证$x_i$(下标)字典序最小……

 1 /**************************************************************
 2     Problem: 1046
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:2116 ms
 7     Memory:1428 kb
 8 ****************************************************************/
 9  
10 //BZOJ 1046
11 #include<vector>
12 #include<cstdio>
13 #include<cstring>
14 #include<cstdlib>
15 #include<iostream>
16 #include<algorithm>
17 #define rep(i,n) for(int i=0;i<n;++i)
18 #define F(i,j,n) for(int i=j;i<=n;++i)
19 #define D(i,j,n) for(int i=j;i>=n;--i)
20 #define pb push_back
21 using namespace std;
22 inline int getint(){
23     int v=0,sign=1; char ch=getchar();
24     while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();}
25     while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();}
26     return v*sign;
27 }
28 const int N=1e4+10,INF=~0u>>2;
29 typedef long long LL;
30 /******************tamplate*********************/
31 int n,m,a[N],b[N],len,f[N],ans[N];
32 int Find(int x){
33     int l=1,r=len,mid,ans=len+1;
34     while(l<=r){
35         mid=l+r>>1;
36         if (b[mid]<=x) ans=mid,r=mid-1;
37         else l=mid+1;
38     }
39     return ans;
40 }
41 int main(){
42 #ifndef ONLINE_JUDGE
43     freopen("1046.in","r",stdin);
44     freopen("1046.out","w",stdout);
45 #endif
46     n=getint();
47     F(i,1,n) a[i]=getint();
48     D(i,n,1){
49         int x=Find(a[i]);
50         f[i]=x; b[x]=a[i];
51         if (x>len) len=x;
52     }
53     m=getint();int x;
54     while(m--){
55         x=getint();
56         if (len<x) {puts("Impossible");continue;}
57         int last=0;
58         F(i,1,n)
59             if (f[i]>=x && a[i]>last){
60                 printf("%d",a[i]);
61                 if (x!=1) printf(" ");
62                 last=a[i];
63                 x--;
64                 if (x==0) break;
65             }
66         puts("");
67     }
68     return 0;
69 }
70 
View Code

P.S.一开始想成数值字典序最小了……如果是数值字典序的话也可做,方法类似?(以下内容与本题解法无关)

  预处理出来一张表,在这张表 f 中,f[i]里存的是最长上升子序列长度>=i 的数的下标,且满足这些数(即下标对应的数)是单调递减的。

  感觉说起来好怪……贴下代码吧

  就是满足下标单调增,但值单调减

 1 #include<vector>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<iostream>
 6 #include<algorithm>
 7 #define rep(i,n) for(int i=0;i<n;++i)
 8 #define F(i,j,n) for(int i=j;i<=n;++i)
 9 #define D(i,j,n) for(int i=j;i>=n;--i)
10 #define pb push_back
11 using namespace std;
12 inline int getint(){
13     int v=0,sign=1; char ch=getchar();
14     while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();}
15     while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();}
16     return v*sign;
17 }
18 const int N=1e4+10,INF=~0u>>2;
19 typedef long long LL;
20 /******************tamplate*********************/
21 int n,a[N],best,ans[N];
22 vector<int>f[N];
23 vector<int>::iterator tmp;
24 int main(){
25 #ifndef ONLINE_JUDGE
26     freopen("1046.in","r",stdin);
27     freopen("1046.out","w",stdout);
28 #endif
29     n=getint();
30     F(i,1,n) a[i]=getint();
31     f[1].pb(1); best=1;
32     F(i,2,n){
33         D(j,best,2){
34             if (a[i]<a[f[j][f[j].size()-1]] && 
35                 a[f[j-1][f[j-1].size()-1]]<a[i])
36                 f[j].pb(i);
37         }
38         if (a[i]>a[f[best][f[best].size()-1]]){
39             best++;
40             f[best].pb(i);
41         }
42         if (a[i]<a[f[1][f[1].size()-1]]) f[1].pb(i);
43     }
44     int m=getint(), x;
45     while(m--){
46         x=getint();
47         if (f[x].empty()){puts("Impossible");continue;}
48         ans[x]=f[x][f[x].size()-1];
49         D(i,x-1,1){
50             tmp=lower_bound(f[i].begin(),f[i].end(),ans[x]);
51             tmp--;
52             ans[i]=*tmp;
53         }
54         F(i,1,x) printf("%d ",a[ans[i]]);
55         puts("");
56     }
57     return 0;
58 }
View Code

  也是贪心地去找最优解>_>

1046: [HAOI2007]上升序列

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2866  Solved: 960
[Submit][Status][Discuss]

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

Source

[Submit][Status][Discuss]
原文地址:https://www.cnblogs.com/Tunix/p/4430518.html