【基础算法】最长上升子序列
时间限制: 1 Sec 内存限制: 64 MB题目描述
给定一个整数序列A1A2A3….An。求它的一个递增子序列,使子序列的元素个数尽量多,元素不一定要求连续。
输入
第1行:1个整数n(1<=n<=5000),表示序列中元素的个数.
第2行-n+1行:每行1个整数x(-1000<=x<=1000),第i+1行表示序列中的第i个元素。
输出
第1行:1个整数k,表示最长上升子序列的长度。
第2行:k个用单个空格分开的整数,表示找到了最长上升子序列。如果有多个长度等于k的子序列,则输出最靠前的1个。
样例输入
8
1
3
2
4
3
5
4
6
样例输出
5
1 3 4 5 6
#--------------------------------------------------------------------------------#
总之,这是一种递推的思想,你需要将当前的状态(原谅我我找不出更好的词)用之前的状态表示出来,最后只需一个循环,你就可以通过f[1]得到f[n](通常是指最优解问题)。
好的,对于这道题,我们可以列出一个方程(且称这猥琐的东西为方程):
f[i]=max{f[1],f[2],...,f[i-1]}+1
这是什么意思呢?首先:
f[i]表示的就是第i个人(必须要有第i个人)之前最多可以抓多少个苦力,显然,f[1]=1,因为第1个人之前没有人比TA高……
max应该不陌生,就是取f[1],f[2],...,f[i-1]中最大的。
但是,如果你明白了一些,肯定会问:第i个人不一定比1-(i-1)人都要高啊?
所以,还是让j从1到i-1开始枚举f[j],但是,在选最大的时候要再加一个条件:a[j]<a[i],a是存身高(原始序列)的数组。
但是,a[j]<a[i]只是表示第j个人比第i个人小,不表示f[j]中的每一个人都比i要矮啊?不用担心,既然这个人已经进了f[j],TA就一定比j要矮,而j比i矮,所以,f[j]中包含的每一个人都会比i要矮。
于是就有了:if(f[j]>maxi&&a[j]<a[i]) maxi=f[j],这样就选出来了。
为什么最后还要加1呢?看看前面:“f[i]表示的就是第i个人(必须要有第i个人)”,所以,算上i,还要再加1。
关于求最多苦力就是这样了。
接下来还有,要输出苦力的编号:
在找maxi的时候,就把j记录下来,最后递归输出,这部分详情看代码。
有问题欢迎留言~
代码:
#include<cstdio>
#include<cstring>
int a[5005],n,maxans,flag=1;//a为原始数组,n是人数量,maxans表示最多的苦力在f中的哪个位置
int f[5005],ans[5005];//f是动规数组,ans是存最多苦力的编号的数组
void print(int x)
{
if(ans[x]!=0)//直到父亲为0(没有了)
print(ans[x]);
if(flag) printf("%d",a[x]),flag=0;
else printf(" %d",a[x]);//flag控制多余空格
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);//读入
for(int i=1;i<=n;i++)
{
int maxi=0;//寻找f[1],f[2],...,f[i-1]中最大且符合条件的一个
for(int j=1;j<i;j++)
if(f[j]>maxi&&a[j]<a[i])
{
maxi=f[j];
ans[i]=j;//可以理解为i的父亲(就是i是由j推来的)
}
f[i]=maxi+1;
if(f[i]>f[maxans])//找最大的
maxans=i;
}
printf("%d\n",f[maxans]);
print(maxans);
}
By WZY