比如输入
8
5 9 4 1 3 7 6 7
输出
4
思想如下(自己不知道怎么说,所以借鉴别人的说法吧):
假设要寻找最长上升子序列的序列是a[n],然后寻找到的递增子序列放入到数组b中。
(1)当遍历到数组a的第一个元素的时候,就将这个元素放入到b数组中,以后遍历到的元素都和已经放入到b数组中的元素进行比较;
(2)如果比b数组中的每个元素都大,则将该元素插入到b数组的最后一个元素,并且b数组的长度要加1;
(3)如果比b数组中最后一个元素小,就要运用二分法进行查找,查找出第一个比该元素大的最小的元素,然后将其替换。
在这个过程中,只重复执行这两步就可以了,最后b数组的长度就是最长的上升子序列长度。例如:如该数列为:
5 9 4 1 3 7 6 7
那么:
5 //加入
5 9 //加入
4 9 //用4代替了5
1 9 //用1代替4
1 3 //用3代替9
1 3 7 //加入
1 3 6 //用6代替7
1 3 6 7 //加入
最后b中元素的个数就是最长递增子序列的大小,即4。
要注意的是最后数组里的元素并不就一定是所求的序列,
例如如果输入 2 5 1
那么最后得到的数组应该是 1 5
而实际上要求的序列是 2 5
所以这种思路还是有一点缺陷的,就是不能打印出最长递增子序列的长度,最后查找序列中元素的时候如果是普通的查找的话时间复杂度为O(n),整个算法的时间复杂就变成了O(n*n),所以为了降低时间复杂度,用了二分查找(时间复杂度(O(logn))),整个时间复杂度就是O(nlogn)了,还不错 ^-^
代码如下:
#include<iostream>
#include<string.h>
using namespace std;
int BinarySearch(int a[],int num,int low,int hight)
{
int mid;
while(low<=hight)
{
mid = (low+hight)/2;
if(num>=a[mid])low=mid+1;
else hight = mid-1;
}
return low;
}
int dp(int a[],int n)
{
int len;
int *list = new int[n+2];
memset(list,0,n+2);
int count = 1;
list[count] = a[1];
for(int i=2;i<=n;i++)
{
if(list[count]>a[i])
{
len = BinarySearch(list,a[i],1,count);
list[len] = a[i];
}
else if(list[count]<a[i]) list[++count]=a[i];
}
return count;
}
int main()
{
int q;cin>>q;
while(q--)
{
int n;cin>>n;
int *a = new int[n+1];
for(int i=1;i<=n;i++)cin>>a[i];
cout<<dp(a,n)<<endl;
}
}