*思路
1.暴力
从第一个数开始倒最后一个,暴力枚举它(记为a[i])后面的每一个数,如果(a[j]>a[i])符合条件,就函数递归自身,从a[j]开始像刚才的步骤查a[j]后面的每个数,并加一(a[j]自身算一个),还要取个最大值,再返回最大值
复杂度是:n个数,有2^n个子序列,每个数查n次,所以是O(2^n*n)
2.记忆化
想象函数递归的一棵树,发现有重复计算,所以用哈希表存下算过的,每次递归调用的时候,先查表看是否算过,算过就直接返回已知值,这个就是记忆化搜索(所以说动归是空间换时间)(递归数的剪枝)
3.迭代
观察求解过程,再进行简化。可以发现,从后往前,有几个是多算的而且每次都会用上,不如就从后往前计算,用两个for,外面遍历数列的尾到头,里面求max里面的f[]。
f[i]=max(f[i-1],f[i-2],,,,f[n])+1;
f[i-1]=max(f[i-2],f[i-3],,,,f[n])+1;
f[i-2]=max(f[i-3],f[i-4],,,,f[n])+1;
,,,,,,
f[n-1]=max(f[n])+1;
f[n]=1;
//f[i]表示a[i]及其后面的数的最长递增子序列个数
*acwing的学习笔记
1.状态表示
首先从一维思考看行不行,再扩展,就是怕搞复杂。此题用f[i]
(1)集合
f[i]表示所有以第i个数结尾的上升子序列的集合
(2)属性
这个集合是个数,题目规定它的属性,此题求最大值
2.状态计算
这个集合既然是个集合,就可以分类,按照此序列的倒数第二个数是什么来分
{0}只有a[i]
{1}表示倒数第二个数是a[1]
假设这个倒数第二个数是a[j],则 f[i] = max(f[i], f[j] + 1),j为0~i-1
3.时间复杂度
O(n^2)=状态数n*转移数n
4.代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int n;
int a[N],f[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int mx=1;//不能丢啊!至少还有一个数本身的子序列个数为1噢
for(int i=1;i<=n;i++)
{
f[i]=1;//不能丢啊!
for(int j=1;j<i;j++)
{
if(a[j]<a[i])//找数列中以每个数结尾的之前的数判断大小,不要搞反符号
f[i]=max(f[i],f[j]+1);//如果符合条件,我可要可不要,所以才做max判断
}
mx=max(mx,f[i]);//再比较每个数的
}
cout<<mx;
return 0;
}