E03 线性DP 最长上升子序列 - 董晓 - 博客园 (cnblogs.com)

给定一长度位为n的序列,求数值严格单调递增的子序列的长度最长是多少

f[i]:所有以第i个数结尾的上升子序列的长度

#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];
	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);
	}
	int res = 0;
	for(int i = 1; i<=n; i++) 
		res = max(res, f[i]);
	cout<<res;
	return 0;
}


#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=1010;
int n, a[N];
int f[N];//动态转移:第i个数的上升子序列
//递推式:1)由小到大(最优子结构)
//       2)由过去推现在(无后效性)

int main(){
  cin>>n;
  for(int i=1; i<=n; i++) cin>>a[i];
  
  for(int i=1; i<=n; i++) f[i]=1;
  for(int i=1; i<=n; i++)//遍历
    for(int j=1; j<i; j++)//动态更新
      if(a[j]<a[i]) f[i]=max(f[i],f[j]+1);
  
  int res=0;
  for(int i=1; i<=n; i++) res=max(res,f[i]);
  cout<<res;
}

线性DP,二分优化:

把内层循环改为二分查找(logn)-->(nlogn),新定义一个b数组存储上升子序列;关键要动态更新b数组

因为要求的是长度,所以b数组中具体是啥无所谓(不一定是上升子序列),只需更新lenth

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=100010;
int n, a[N];
int len, b[N]; //记录上升子序列

int main(){
  scanf("%d", &n);
  for(int i=0; i<n; i++) scanf("%d", &a[i]);

  b[0]=-2e9;                              //哨兵
  for(int i=0; i<n; i++)
    if(b[len]<a[i]) b[++len]=a[i];        //新数大于队尾数,则插入队尾
    else *lower_bound(b,b+len,a[i])=a[i]; //替换第一个大于大于a[i]的数(贪心)

  printf("%d\n", len);
}