什么是最长上升子序列?
这个最长上升子序列,有的叫最长递增子序列,有的叫最长不下降子序列(这个可能有点区别,相当于可以有相等的情况) 而最长上升子序列是每个数逐渐递增
这里的子序列可以不连续 但是要是递增的
举个例子
1 2 4 1 3 4
这里最长的上升子序列为:1 2 3 4
如何求最长上升子序列?
算法核心思想:
1设置一个一维数组dp[i]:表示以a[i]结尾的最长上升子序列(数组下标这里从1开始)
2依次比较定义个J遍历1-i之间的所有情况
具体步骤:若a[j]<a[i]
那么以a[i]结尾的最长上升子序列等于以a[j]结尾的上升子序列+1
即:dp[i]=max(dp[j],dp[i]);
3最后我们需要再dp[i]中选取一个最大的 就是最长上升子序列的长度
举个例子
1 2 4 1 3 4
dp[1]=1 因为当序列只有一个数的时候最长为1
dp[2]=1+1=2 由于1<2 所以dp[2]的最长上升子序列等于dp[1]+1;
dp[3]=2+1
这里需要比较两次 我们每次都是从第一个数比较到i-1个数
1<4 所以dp[3]=dp[1]+1=2
2<4 所以dp[3]=dp[2]+1=3
当然这里不是每次都更新 我们要保留最大的 所以要dp[3]=max(dp[3],dp[j]); j从1-2
举个反例:2314
当我们求4结尾的手 dp[4]=dp[2]+1=3;
但后面由于1比2 3都小 以1结尾的子序列只有自己一个 所以dp[3]=1
而若直接更新 dp[4]=dp[3]+1=2
显然是错的 前面那种情况更大 所以我们在与前面比较的时候需要取最大值
随后dp[4]=1
dp[5]=3; 123 和 12我们取最大值dp[2]转移过来的 而不是dp[4]转移过来的
dp[6]=4 即子序列1234 是由dp[5]转移过来的 因为3<4 而dp[5]又是前面最大的一个
由于部分题目需要求出最大长度和具体序列
所以我们需要开辟一个数组g,记录dp[i]由那个状态转移过来的 其实就是在每次max更新的时候记录接在谁后面
#include <iostream>
using namespace std;
const int N=210;
int a[N],f[N],g[N],s[N];
int main()
{
int n,ans=-1;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<=a[i]) //前一个比我小才可以讨论
{
if(f[i]<f[j]+1) //新方案更大才更新
{
f[i]=max(f[i],f[j]+1);
g[i]=j;//记录状态由哪里转移过来 只有更新了才记录 所以再if循环内
}
}
}
}
int k=0;
for(int i=1;i<=n;i++)//这一步是为了求出最长上升子序列是哪一个结尾的 记录他的下标
{
if(f[i]>ans)
{
ans=f[i];
k=i;
}
}
printf("max=%d\n",ans);
for(int i=0;i<ans;i++)
{
s[i]=a[k];//将序列每个数字读入数组 因为有些题目有输出顺序
k=g[k];//更新g[k]表示dp[k]是的上一个数的下标 然后我们让k=g[k]
}
for(int i=ans-1;i>=0;i--)cout<<s[i]<<' ';
return 0;
}