最长上升子序列

最长上升子序列

题目链接:

  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;
}
相关推荐
xin007hoyo2 小时前
算法笔记.染色法判断二分图
数据结构·笔记·算法
এ᭄画画的北北5 小时前
力扣-234.回文链表
算法·leetcode·链表
八股文领域大手子5 小时前
深入理解缓存淘汰策略:LRU 与 LFU 算法详解及 Java 实现
java·数据库·算法·缓存·mybatis·哈希算法
__lost6 小时前
C++ 解决一个简单的图论问题 —— 最小生成树(以 Prim 算法为例)
算法·图论·最小生成树·prim算法
wuqingshun3141597 小时前
蓝桥杯 11. 打印大X
数据结构·算法·职场和发展·蓝桥杯·深度优先
Blossom.1187 小时前
量子网络:构建未来通信的超高速“高速公路”
网络·opencv·算法·安全·机器学习·密码学·量子计算
A林玖8 小时前
【机器学习】朴素贝叶斯
人工智能·算法·机器学习
六边形战士DONK8 小时前
神经网络基础[损失函数,bp算法,梯度下降算法 ]
人工智能·神经网络·算法
wuqingshun3141598 小时前
蓝桥杯 2. 确定字符串是否是另一个的排列
数据结构·c++·算法·职场和发展·蓝桥杯
小刘|8 小时前
JVM 自动内存管理
java·jvm·算法