最长上升子序列

最长上升子序列

题目链接:

  1. https://www.acwing.com/problem/content/897/ (元素小于 1000 1000 1000个)
    2. https://www.acwing.com/problem/content/898/ (元素小于 100000 100000 100000个)

3.7-4 最长上升子序列 - 云上第二次周赛题

解法:

  • 动态规划 O ( n 2 ) O(n^2) O(n2)复杂度过高
  • 贪心加二分 O ( n l o g n ) O(nlogn) O(nlogn)推荐
  • d p dp dp加线段树 O ( n l o g n ) O(nlogn) O(nlogn)太难

算法一:动态规划

状态表示: f i fi fi

  • f i fi fi 表示从第一个数开始算,以第 i i i 个数结尾的最长上升子序列

状态计算:

  • 如果我们选第 1 1 1 个数,且第 1 1 1 个数小于第 i i i 个数,那么对应的就是 f 1 f_1 f1
  • 如果我们选第 2 2 2 个数,且第 2 2 2 个数小于第 i i i 个数,那么对应的就是 f 2 f_2 f2
  • 如果我们选第 j j j 个数,且第 j j j 个数小于第 i i i 个数,那么对应的就是 f j f_j fj
  • 所以状态转移方程就是 f i = max ⁡ 1 ≤ j < i & a j < a i { f j + 1 } f_{i}=\max {1 \leq j<i \& a{j}<a_{i}}\left\{f_{j}+1\right\} fi=max1≤j<i&aj<ai{fj+1}

初始化:

  • 因为每一个数以自己结尾的最长上升子序列的长度为 1 1 1 ,所以初始 f i 1 ≤ i ≤ n = 1 \underset{1 \leq i \leq n}{f_{i}}=1 1≤i≤nfi=1

答案:

  • 根据定义,由于每一个数都有可能成为最长上升子序列的最后一个数,所以答案就是 m a x 1 ≤ i ≤ n f i \underset{1 \leq i \leq n}{max}{f_i} 1≤i≤nmaxfi
cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
//f[i]表示以第i个元素结尾的最大长度
const int N=1e3+10;
int n;
int q[N],f[N];
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	cin>>q[i];
	for (int i=1;i<=n;i++)
	{
		f[i]=1;
		for (int j=1;j<i;j++)
		{
			if(q[i]>q[j])
			f[i]=max(f[i],f[j]+1);
		}
	}
	cout<<*max_element(f+1,f+1+n)<<endl;
	return 0;
}

算法二:贪心+二分

  • 状态表示:f[i]表示长度为 i i i的最长上升子序列,末尾最小的数字 。(长度为 i i i的最长上升子序列所有结尾中,结尾**最小 m i n min min**的)即长度为 i i i的子序列末尾最小元素是什么。
  • 状态计算:对于每一个w[i], 如果大于 f[cnt-1] (下标从 0 0 0开始, c n t cnt cnt长度的最长上升子序列,末尾最小的数字),那就 c n t + 1 cnt+1 cnt+1,使得最长上升序列长度 + 1 +1 +1,当前末尾最小元素为w[i]。 若w[i]小于等于 f[cnt-1],说明不会更新当前的长度,但之前末尾的最小元素要发生变化,找到第一个 大于或等于 (这里不能是大于) w[i],更新以那时候末尾的最小元素。
  • f[i]一定以一个单调递增的数组,所以可以用二分法来找第一个大于或等于w[i]的数字。
cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int N=1e3+10;
int n;
int w[N],f[N];
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
	cin>>w[i];
	int len=0;//f数组的长度
	f[++len]=w[1];
	for (int i=2;i<=n;i++)
	{
		if(w[i]>f[len])f[++len]=w[i];
		//比它小,二份一个位置给它放
		else
		f[lower_bound(f+1,f+1+len,w[i])-f]=w[i];//lower_bound返回一个大于等于该数指针,该指针减首地址就是下标
	}
	cout<<len<<endl;//得到的序列不一定合法
	return 0;
}
cpp 复制代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int LIS(const vector<int> &a){
    vector<int> result;
    for (int n:a) {
        auto it= lower_bound(result.begin(),result.end(),n);
        if (it==result.end()) result.push_back(n);
        else *it=n;
    }
    return result.size();
}

int LIS_dp(vector<int> &a){
    int n=a.size();
    if (n==0) return 0;
    vector<int> dp(n,1);
    int maxLen=1;
    for (int i = 1; i < n; ++i) {
        for (int j = 0; j < i; ++j) {
            if (a[i]>a[j]) {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        maxLen=max(maxLen,dp[i]);
    }
    return maxLen;
}

int main(){
    int n;
    cin>>n;
    vector<int> a(n);
    for (int &it:a) cin>>it;
    cout<<LIS(a)<<endl;
    return 0;
}
相关推荐
进击的小头12 分钟前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
咖丨喱2 小时前
IP校验和算法解析与实现
网络·tcp/ip·算法
罗湖老棍子2 小时前
括号配对(信息学奥赛一本通- P1572)
算法·动态规划·区间dp·字符串匹配·区间动态规划
fengfuyao9852 小时前
基于MATLAB的表面织构油润滑轴承故障频率提取(改进VMD算法)
人工智能·算法·matlab
机器学习之心2 小时前
基于随机森林模型的轴承剩余寿命预测MATLAB实现!
算法·随机森林·matlab
一只小小的芙厨2 小时前
寒假集训笔记·树上背包
c++·笔记·算法·动态规划
庄周迷蝴蝶3 小时前
四、CUDA排序算法实现
算法·排序算法
以卿a3 小时前
C++(继承)
开发语言·c++·算法
I_LPL3 小时前
day22 代码随想录算法训练营 回溯专题1
算法·回溯算法·求职面试·组合问题
金融RPA机器人丨实在智能3 小时前
2026动态规划新风向:实在智能Agent如何以自适应逻辑重构企业效率?
算法·ai·重构·动态规划