前言:
我是蒟蒻,请多指教
B3647 最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 n(n≤5000) 个不超过 10^6 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
朴素版本:
进行动态规划时首先要先明白状态时如何转移的,观察样例我们可以发现每一个数字对于它的最长上升子序列是由他前面的数字的长度在累加上它的状态完成的即:
f[i]=f[j]+1
我们只需要在i之前找到f[j]的最大值(此时发f[j]需要小于a[i])在加上当前的数字就可以了
for(int j=i-1;j>=0;j--)
{
if(a[i]>a[j] && cnt<f[j])
{
t=j;
cnt=f[j];
}
}
枚举法寻找符合要求的数
f[i]=f[t]+1;
连接长度
for(int i=1;i<=n;i++)
{
ans=std::max(ans,f[i]);
}
但我们仍要注意在最后寻找最大值
AC代码:
#include<bits/stdc++.h>
#define N 5010
int f[N],a[N],n;int main(){
std::cin>>n;
for(int i=1;i<=n;i++)
{
std::cin>>a[i];
}
int t,cnt=0;
for(int i=1;i<=n;i++)
{
cnt=-1;
for(int j=i-1;j>=0;j--)
{
if(a[i]>a[j] && cnt<f[j])
{
t=j;
cnt=f[j];
}
}
f[i]=f[t]+1;
}
int ans=-1;
for(int i=1;i<=n;i++)
{
ans=std::max(ans,f[i]);
}
std::cout<<ans;
return 0;
}
优化版本(二分):
在朴素版本的基础上,我们不难发现我们可以使用二分的方式来进行时间优化,枚举寻找最大值过于浪费时间了,如果我们定义一个数组d并将长度为i的最小数存入d[i]中,然后在最后找到最大长度求解,我们便可以节省很多的时间
首先是二分:
while(l<r)
{
mid=(l+r+1)/2;
if(a[i]>d[mid])
l=mid;
else
r=mid-1;
}
这一部分的操作是不断寻找上述符合要求的数,需要注意的是
mid=(l+r+1)/2;
加1的原因是防止我们的二分死循环,并注意r+1才是我们真正的赋值对象
d[r+1]=a[i];
对于新的数字进行更新,保证这是最小值,只有这样,在下一次找到了一个更大的数字时我们才能进行新的更新
len=std::max(len,r+1);
不断寻找最大值;
与朴素版的代码功能一样
for(int i=1;i<=n;i++)
{
ans=std::max(ans,f[i]);
}
AC代码:
#include<bits/stdc++.h>
#define N 5010
int d[N],a[N];int n;
int main(){
std::cin>>n;
for(int i=1;i<=n;i++)
{
std::cin>>a[i];
}
int len=0;
for(int i=1;i<=n;i++)
{
int l=0,r=len,mid;
while(l<r)
{
mid=(l+r+1)/2;
if(a[i]>d[mid])
l=mid;
else
r=mid-1;
}
len=std::max(len,r+1);
d[r+1]=a[i];
}
std::cout<<len;
return 0;
}