您的位置:首页 > 其它

NYOJ 题目214 单调递增子序列(二) dp+二分

2016-05-04 22:00 281 查看


单调递增子序列(二)

时间限制:1000 ms  |  内存限制:65535 KB
难度:4

描述

给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度。

如:1 9 10 5 11 2 13的最长单调递增子序列是1 9 10 11 13,长度为5。

输入有多组测试数据(<=7)

每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的下一行里有n个整数,表示数列中的所有元素.每个整形数中间用空格间隔开(0<n<=100000)。

数据以EOF结束 。

输入数据保证合法(全为int型整数)!
输出对于每组测试数据输出整形数列的最长递增子序列的长度,每个输出占一行。样例输入
7
1 9 10 5 11 2 13
2
2 -1

样例输出
5
1


思路:求最长单调子序列,由于数据量较大,传统的dp方法时间复杂度O(n^2)肯定不可行,所以就用了二分的方法,时间复杂度达到O(nlogn)。

具体方法:加一个maxv[]数组,maxv[i]代表长度为i的单调递增子序列最大元素的最小值。遍历a[]数组,每次都根据a[i]的值二分查找a[i]合适的位置,然后更新maxv[i]数组。maxv[len]数组的长度就是最长上升子序列的长度。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int maxv[100005];
int a[100005];
int binarysearch(int x,int len){//寻找maxv[i]中刚好大于等于x的位置
int left,right,mid;
left=1;
right=len;
while(left<=right){
mid=left+(right-left)/2;
if(maxv[mid]==x){
return mid;
}
else if(maxv[mid]<x){
left=mid+1;
}
else {
right=mid-1;
}
}
return left;
}
int main(){
int n,i,j,len;
while(scanf("%d",&n)!=EOF){
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
}
maxv[1]=a[1];
len=1;
for(i=2;i<=n;i++){
if(a[i]>maxv[len]){//如果刚好大于目前maxv[i]中最大的,直接插到最后面
maxv[++len]=a[i];
}
else{
int pos=binarysearch(a[i],len);//二分查找
maxv[pos]=a[i];//将a[i]插入合适的位置
}
}
cout<<len<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  LIS 二分查找 dp