最长上升子序列

最长上升子序列

题目链接:

  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;
}
相关推荐
算AI13 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
hyshhhh15 小时前
【算法岗面试题】深度学习中如何防止过拟合?
网络·人工智能·深度学习·神经网络·算法·计算机视觉
杉之16 小时前
选择排序笔记
java·算法·排序算法
烂蜻蜓16 小时前
C 语言中的递归:概念、应用与实例解析
c语言·数据结构·算法
OYangxf16 小时前
图论----拓扑排序
算法·图论
我要昵称干什么16 小时前
基于S函数的simulink仿真
人工智能·算法
AndrewHZ17 小时前
【图像处理基石】什么是tone mapping?
图像处理·人工智能·算法·计算机视觉·hdr
念九_ysl17 小时前
基数排序算法解析与TypeScript实现
前端·算法·typescript·排序算法
守正出琦17 小时前
日期类的实现
数据结构·c++·算法
ChoSeitaku17 小时前
NO.63十六届蓝桥杯备战|基础算法-⼆分答案|木材加工|砍树|跳石头(C++)
c++·算法·蓝桥杯