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

导弹拦截

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

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

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
所以,如果ia[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]
那么,i 这个位置就是字典序最小的最长上升子序列的其中一个



//Hello. I'm Peter.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"< 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]





你可能感兴趣的:(算法设计)