最长上升子序列是常见的DP问题.数组b[1]~b[n]中存放着n个数字,
从1~n都有,顺序不定。
第一种方法是n*n时间复杂度的算法,利用一个数组d[i](i从1到n)存放着以b[i]作为结尾的最长子序列的长度,则状态转换方程为:
d[i]=max(d[j]+1) (b[i]>b[j] ,i>j) 边界是b[1]=1;
int n,b[n+1],d[n+1];//b[n+1]中存放原来的数组,d[n+1]中存放最长上升子序列的长度
int p[n+1];//p[i]中存放着以b[i]作为最长子序列结尾的上一个字符的下标 如果最长子序列就是本身则p[i]=i;
void output(int i)//打印以b[i]结尾的最长子序列
{
if(p[i]=i)
{
cout<<p[i]<<" ";
}
else
{
output(p[i]);
cout<<i<<" ";
}
}
int lis()
{
for(int i=1;i<=n;i++)
{
p[i]=i;//默认每个b[i]都是单独的最长子序列
d[i]=1;//初始化以b[i]结尾的最长上升子序列长度为1
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
{
if(b[i]>b[j]&&d[i]<d[j]+1)
{
d[i]=d[j]+1;
p[i]=j;
}
}
int maxlength=d[1];
for(int i=1;i<=n;i++)
if(maxlength<d[i])
maxlength=d[i];
return maxlength;
}
第二种算法的复杂度为nlogn在n很大的情况下可以有效得减少计算的时间,在这种算法中用d[len]存储使得最长上升子序列长度为len的最小的结尾,这里为什么要存储最小的结尾呢,因为只有保证d[len]中存放的是最小的
则下次检查b[i]的时候才最容易更新len使得len++,这样才获得最大的len。
在检测b[i]的时候分两种情况
(1).b[i]>d[len] 则len++;d[len]=b[i]
(2)b[i]<=d[len] 则在1~len直接寻找j 使得d[j-1]
#include <iostream>
#include <sstream>
using namespace std;
const int NMAX=20;
int len;
int b[NMAX+1],d[NMAX+1];
int binary_search(int i)//而非查找大于等于i的最小下标
{
int left=1,right=len,middle;
while(left<right)
{
middle=left+(right-left)/2;
if(d[middle]>=i)right=middle;
else left=middle+1;
}
return left;
}
int getscore(int n)
{
len=1;//len为当前最大上升子序列的长度
d[1]=b[1];//d[i]中存放的是最长上升子序列长度为i的最小结尾
for(int i=2;i<=n;i++)
{
if(b[i]>d[len])
{
len++;//更新最长上升子序列的长度
d[len]=b[i];//更新最长上升子序列的末尾
}
else
{
int num=binary_search(b[i]);//求出d[1]~d[len]中>=b[i]的最小值的下标
d[num]=b[i];//更新num下标所对应的的最小结尾
}
}
return len;
}