最长上升子序列(LIS)的 普通解法 和 二分解法
状态转移方程:
dp [ i ] = max { 1 , dp[ j ] + 1 } ( j < i && a[ j ] < a[ i ] )
先来介绍普通解法:复杂度O(n^2)
题目: 输入n个数,求出其中的最长上升子序列。
#include<iostream>
using namespace std;
int main()
{
int a[100];//输入数字
int dp[100];//用于储存以a[i]结尾的最长上升子序列的 长度
int n,m,j,k,i,T;
cin>>n;
for (i=0;i<n;i++)
{
cin>>a[i];
dp[i] = 1;//一开始,所有的长度都为 1
}
int ans = -1;//记录d[i]中最大的数,也就是答案
for (i=0;i<n;i++)
{
for (j=0;j<i;j++)
{
if (a[i]>a[j] && dp[j]+1 > dp[i])//对每一个a[i]遍历它之前的,如果可以把
{ //这个a[i]插在后面,还能让dp[i]增大,那么就加上去
dp[i] = dp[j] + 1;
}
}
ans = max(ans,dp[i]);//记录最大的ans
}
cout<<ans<<endl;
return 0;
}
再来介绍二分算法:复杂度O(n*logn)
这里介绍下 STL 的 upper_bound( ) 函数的用法:
upper_bound( a,a+n ,x) : 在数组 a 中 找到 第一个 大于 x 的 数字的 指针。
那么 upper_bound( a,a+n ,x) - a 就表示 第一个 大于 x 的 数字的 数组的下标。
注意: 数组 a 赋值时要从 下标 0 开始,才能使用这个函数。
思路:
用 lis 数组储存 最长上升子序列的 序列 的最优情况 。
在大于lis [ len ]时 len++,在小于lis[ len ]时可以将arr[ i ]在 lis 中的数进行替换掉。所以此算法主要是在不停的找最合适的起点和最合适的终点。
举例:
输入数组:2 1 5 3 6 4 8 9 7 显而易见 答案为:5
以下是 lis 数组的变化情况 :
第一步:2 替换
第二步:1 替换
第三步:1 5 加入
第四步:1 3 替换
第五步:1 3 6 加入
第六步:1 3 4 替换
第七步:1 3 4 8 加入
第八步:1 3 4 8 9 加入
第九步:1 3 4 7 9 替换
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10000];
int lis[10000];
int n,m,j,k,i,T;
cin>>n;
for (i=0;i<n;i++)
cin>>a[i];
int len = 0;
lis[0] = a[0];
for (i=1;i<n;i++)
{
if (a[i] > lis[len])
lis[++len] = a[i];
else
{
int pos = upper_bound(lis,lis+len,a[i]) - lis;
lis[pos] = a[i];
}
}
cout<<len+1<<endl;.//最后答案 +1
return 0;
}