最长递增子序列,首先肯定是动态规划的思想; 有两种思路;
*** 先看第一种:(复杂度O(n^2))
这种思路也是常规思路: 设置一个数组dp[ i] 来表示 以第i 个元素结尾的的最长的递增子序列 ; 然后转移方程可以这样写:
dp[i] =Max (1 , dp[j]+1,) 其中 j : from 1--(i-1); 这种思路也比较容易理解: 找出i 之前的并且末尾项比第i项小的最长的子序列的长度;附代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
int N,list[100005],dp[100005];
while(scanf("%d",&N)!=EOF)
{
for(int i=1;i<=N;i++)
{
scanf("%d",&list[i]);
dp[i]=list[i];
}
for(int i=1;i<N;i++)
{
int tag=-1;
for(int j=1;j<i;j++)
if(list[i]>list[j]&&tag<dp[j])//寻找i 前面的
tag=dp[j];
dp[i]=max(1,tag+1);
}
int m=-1;
for(int i=1;i<=N;i++)
if(m<dp[i]) m=dp[i];
printf("%d\n",m);
}
}
但是很明显可看出,复杂度略大,O(n^2) ; 我们可以想象可以优化的地方:
就是寻找第i项前面的时候, 复杂度为o(n) , 我们可以借助一个辅助数组,来实现二分查找;
这就是第二种方法:
假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。
然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!
附代码:
#include <iostream> #include <stdio.h> using namespace std; int N,a[100005],b[100005],len; int Bin() { b[0]=a[1]; len=1; for(int i=2;i<=N;i++) { int left= 0,right=len-1; while(left<=right) { int mid=(left+right)/2; if(b[mid]<a[i]) left=mid+1; else right=mid-1; } b[left]=a[i]; if(left>=len) len++; } return len; } int main() { while(scanf("%d",&N)!=EOF) { for(int i=1;i<=N;i++) scanf("%d",&a[i]); Bin(); printf("%d\n",len); } }