标签:
简述:
LIS问题,即最长上升子序列问题,经典的解法有序列DP,通过这个算法,可以获得最长上升子序列的各种详细信息。但是,我们有时候只需要求最长上升子序列的长度,但是o(n^2)的时间复杂度太慢了,我们希望有一种算法,可以更快一点。
算法过程
既然动态规划太慢了,那么自然就想到了贪心。下述算法,就是运用了二分+贪心
首先考虑一个序列a:1、3、2、7、5、6、4 (一共7个数),另外一个辅助数组d,最终序列长度为ans
第1个数:d[1] = a[1] = 1 , ans = 1 ;
第2个数:a[2] > a[1] , 所以d[2] = a[2] , ans = 2 ;
第3个数:d[1] < a[3] < d[2] , 此时ans不改变,但是根据贪心法则,d[2] = a[3] ;
第4个数:a[4] > d[2], d[3] = a[4] = 7 , ans = 3 ;
第5个数:a[5] < d[3] , ans不变,根据贪心法则,d[3] = a[5] ;
第6个数:a[6] > d[4] , d[4] = a[6] , ans =4 ;
第7个数:a[7] < d[4] , 为保证算法完整性,d[3] = a[7] (这里替代的数是第一个大于key的数)。
此时ans=4,d数组为:1、2、4、6 。
虽然我们算出了答案,但是我们并不能得到最终的最长子序列,这就是这个算法的不足。
代码:
用poj 2533来测代码,题目链接:poj 2533
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,ans,a[1005],d[1005];
int upper(int key) //二分找到第一个比key大的数
{
int low=1,high=ans,mid;
if(d[ans]<key)
return ans+1;
while(low<high)
{
mid=(low+high)/2;
if(d[mid]<key)
low=mid+1;
else
high=mid;
}
return high;
}
int main()
{
while(~scanf("%d",&n))
{
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
d[1]=a[1],ans=1;
for(int i=2;i<=n;i++)
{
int pos=upper(a[i]);
d[pos]=a[i];
ans=max(ans,pos);
}
printf("%d\n",ans);
}
return 0;
}
总结:
运用二分的思想,虽然达不到找到最长子序列的效果,但是找到答案绰绰有余,所以这个算法还是有它的价值的。
标签:
原文地址:http://blog.csdn.net/fuyukai/article/details/44119471