BZOJ1046 HAOI2007 上升序列 一般DP+贪心

题意:对于一个给定的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.

题解:

首先定义g[i]为从第i个数开始的最长上升子序列的长度。如果当前选择的数的数量+g[i]>=l,由于字典序最小,因此i一定在所求序列中且该询问一定有解。

求g[i]可以将原序列反转后直接跑一边最长上升子序列,那么g[i]=f[n-i+1]。(证明略,自己想想就明白了——其实是我懒得写了- -)

最后注意输出格式。

#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN=10000+2;
int a[MAXN],b[MAXN],f[MAXN],g[MAXN],n,m,l,last_num;

bool cmp(int a,int b){return a>=b;}

int main(){
    cin >> n;
    for(int i=n;i;i--) scanf("%d",a+i);

    memset(b,171,sizeof(b));

    for(int i=1;i<=n;i++){
        f[i]=upper_bound(b,b+n+1,a[i],cmp)-b;
        b[f[i]]=max(b[f[i]],a[i]);
    }

    for(int i=1;i<=n/2;i++) swap(a[i],a[n-i+1]);
    for(int i=1;i<=n;i++) g[n-i+1]=++f[i];//至于为什么要++f[i],是因为用STL求出来的最长上升子序列是不包括自己的。

    cin >> m;
    while(m--){
        scanf("%d",&l);
        last_num=INT_MIN;//注意初始化
        for(int i=1;i<=n && l;i++){
            if(g[i]>=l && last_num<a[i]){
                last_num=a[i],l--;//last_num记录当前已经找到的序列的最后一个元素
                cout << a[i];
                if(!l) cout << endl;
                else cout << " ";//注意输出格式
            }
        }
        if(l) cout << "Impossible" << endl;
    }

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/WDZRMPCBIT/p/6477146.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值