奶牛排队(USACO)( JSOI夏令营2017 day1 T2 ) * * *【O(nlogn)最长上升子序列】

题目

奶牛排队 sort
时间限制:1.0s
内存限制:256MB
问题描述
约翰日常折腾奶牛系列。。。这一次约翰要选出尽可能多的奶牛排成一队。
然而约翰非常的挑剔,他要求奶牛排成的队必须身高和体重都是上升的序列。
问最多可以挑多少头奶牛排成一队?
输入格式
第一行一个正整数n
第二行共有n个数字,分别是奶牛的身高h1,h2……hn。
第三行共有n个数字,分别是奶牛的体重w1,w2……wn。
输出格式
只有一行,为最多的奶牛数量。
样例输入
5
5 7 3 6 8
3 6 7 8 2
样例输出
2
提示
挑出奶牛后是可以重新给他们排队的
评测用例规模与约定
对于40%的数据,1 <= n <= 1000
对于100%的数据,1 <= n <= 100000
对于100%的数据,1 <= hi,wi <= 2147483647

分析

  • 双关键字排序 ( h相同时w从大到小排 )
  • 十万数据量– O( nlogn ) 最长上升子序列( LIS ):栈+二分

O( nlogn ) 最长上升子序列( LIS ):栈+二分算法详解(by RyanWang)***

开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。

这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的”潜力”增大了。

举例:原序列为1,5,8,3,6,7

栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。

代码

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define N 1000001
#define M 10001
#define LL long long
#define oo 0x7fffffff
using namespace std;

int read()
{
    int f=1,s=0;
    char ch=getchar();
    while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){s=(s<<1)+(s<<3)+ch-'0';ch=getchar();}
    return f*s;
}

int m,n,k;
int tot,cnt,ans,top;
int stk[N];
struct node{
    int h,w;
}a[N]; 

bool my_cmp( const node &a , const node &b ) //排序( h相同w从大到小 )  
{
    if( a.h < b.h ) return true;
    if( a.h==b.h && a.w>b.w ) return true;
    return false;
}

void LIS_ex() // O(nlogn)最长上升子序列 
{
    F(i,1,n)
    {
        if( a[i].w > stk[top] ) stk[++top]=a[i].w; //now大于栈顶即入栈 
        else
        {
            int l,r,mid;
            l=1;
            r=top;
            while(l<=r) //二分查找第一个大于now 的元素并替换 
            {
                mid=( l+r )>>1;
                if( a[i].w>stk[mid] ) l=mid+1;
                else r=mid-1;
            }
            stk[l]=a[i].w;
        }
    }

} 

int main()
{
    freopen( "sort.in" ,"r" ,stdin );
    freopen( "sort.out","w" ,stdout );
    n=read();
    F(i,1,n)
    {
        a[i].h=read();
    }
    F(i,1,n)
    {
        a[i].w=read();
    }
    sort( a+1 ,a+n+1 ,my_cmp );
    LIS_ex(); 
    cout<< top <<endl;
    return 0;
}

测试结果

测试结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值