有一个长度为n的数列
a
0
,
a
1
,
a
2
,
…
,
a
n
−
1
a_0,a_1,a_2,…,a_{n-1}
a0,a1,a2,…,an−1
请求出这个序列中最长的上升子序列的长度
样例输入
n =5
a= {4,2,3,1,5}
输出
3 ( 2,3,5)
同样,我们要用dp来解决这个问题的话,首先要找出他的递推式
我们假设 dp【i】= 以ai 为末尾的最长上升子序列的长度。
而这个长度可能是如下:
1.只包含ai一个元素的子序列
2. 在前面满足j<i 并且 a【j】<a【i】的情况下 为dp【j】追加上ai为末尾即dp【j】+1;
这二者之一
所以得到关系式
dp【i】=max{1 , dp【j】+1 (j<i 且 a【j】<a【i】)}
依照此式便可得出代码
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
int main()
{
int dp[100];
int n,i,t,j,k;
int a[100];
scanf("%d",&n);
int max1=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(i=0;i<n;i++)
{
dp[i]=1;
for(j=0;j<i;j++)
{
if(a[j]<a[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
max1=max(max1,dp[i]);
}
printf("%d",max1);
}
便可在o(n2)时间内解决
、、、、、、
此外,在此基础上还有复杂度更低的dp算法。
在前面的基础上我们可以知道,如果某几个子序列的长度相同,那么子序列中最后一位最小的在之后一定最长我们可以利用并记录这一点
设inf=无限大;
我们假设dp【i】=长度为i的上升子序列中,末尾元素的最小值(不存在便是inf)
怎么实现呢?
最开始全部dp【i】的值都化为inf,而后从前到后逐渐更新,对于每一个aj ,如果dp【i-1】<a【j】,那么可以用dp【i】=min(dp【i】,a【j】)
或者可以说,对于每一个a【j】,从dp【0】到dp【j】中搜索出
dp【k】<=a【j】中最小的k值;
可以用lower_bound函数找出这个值
于是代码如下
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
int inf=100000;
int main()
{
int dp[100];
int n,i,t,j,k;
int a[100];
scanf("%d",&n);
int max1=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
fill(dp,dp+n,inf);
for(i=1;i<=n;i++)
{
*lower_bound(dp,dp+n,a[i])=a[i];
}
printf("%d",lower_bound(dp,dp+n,inf)-dp);
}
在最后,只需要找出最近一个inf的位置,即为最长上升子序列
这个算法的复杂度是o(n*logn)
有问题欢迎和我一起交流!!qwq