CDOJ 251 导弹拦截 (LIS,一种找到字典序最小的最长上升子序列的方法)

导弹拦截

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都要高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹,同时,司令部想知道拦截下来的导弹的高度。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

Input

第一行是一个整数 t ,代表case数。 对于每一个case,第一行是一个整数 n(1n100000) ; 第二行是 n 个正整数,表示第 n 枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。数据保证高度不会超过 100000 .

Output

对于每一个case,第一行输出最多能拦截的导弹数,第二行按来袭顺序输出拦截下来的导弹的高度构成的序列,以一个空格隔开。若有不止一种方法可以拦截最多的导弹,输出字典序最小的。

Sample input and output

Sample Input Sample Output
1
5
1 6 3 5 7
4
1 3 5 7
题目地址: http://acm.uestc.edu.cn/#/problem/show/251
显然,最长上升子序列。不会求请去学,推荐《算法入门经典训练指南 》(刘汝佳著)在动态规划章节里有
但此题还要求输出字典序最小怎么办呢?
可以证明,从后往前找,每次找到一个d[i]符合要求,并且其值小于last的,就一定是字典序最小的
为什么?
设a[i]表示第i个数在原数列中是多少,d[i]表示以i结尾时候最长上升子序列的长度是多少,g[i]的含义请看刘汝佳那本书
假设i<j 如果 a[i]<a[j] 那么 一定有d[i]<d[j]
所以,如果i<j 且 d[i]==d[j] 那么 a[i]==a[j] 或者 a[i]>a[j]
当a[i]==a[j]的时候,对于a[i]前面待加入答案的值,和a[j]来说是等价的,因为a[i]==a[j] 不妨取越靠后的越好
如果a[i]>a[j],请注意,此时d[i]==d[j],那么,假设选取a[i]的那一个序列,找到了一个最长上升子序列,这个子序列的字典序一定是
大于含有a[j]的那一个序列的。因为d[i]==d[j]所以含有a[i]的那个序列小于d[i]的答案都可以放在含有a[j]的那个序列中,又a[i]>a[j]
所以a[j]的字典序一定更小

由此就可以知道,当跑完一发LIS后,只需要从数列从后往前找,找到一个希望的d[i],并且a[i]<last(last表示序列中后一个的值,这个值已经找到)
那么,i 这个位置就是字典序最小的最长上升子序列的其中一个



//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi 3.1415926535898
#define eps 1e-6
#define MOD 10007
#define MAXN 100100
#define N 10100
#define M
int a[MAXN],g[MAXN],d[MAXN],ans[MAXN];
int n;
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d",&n);
        repin(i,1,n)
        {
            scanf("%d",a+i);
            g[i]=INT;
        }
        int k,maxi=1;
        repin(i,1,n)
        {
            k=(int)(lower_bound(g+1,g+1+n,a[i])-g);
            g[k]=a[i];
            d[i]=k;
            maxi=max(maxi,d[i]);
        }
        int last=INT,t=maxi;
        depin(i,n,1)
        {
            if(!t) break;
            if(d[i]==t && a[i]<last)
            {
                last=a[i];
                ans[t]=a[i];
                t--;
            }
        }
        printf("%d\n",maxi);
        bool first=true;
        repin(i,1,maxi)
        {
            if(first) first=false;
            else printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
}





  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值