【洛谷 B3637】最长上升子序列 题解(动态规划+最长上升子序列)

发布于:2024-04-26 ⋅ 阅读:(20) ⋅ 点赞:(0)

最长上升子序列

题目描述

这是一个简单的动规板子题。

给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。

最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。

输入格式

第一行,一个整数 n n n,表示序列长度。

第二行有 n n n 个整数,表示这个序列。

输出格式

一个整数表示答案。

样例 #1

样例输入 #1

6
1 2 4 1 3 4

样例输出 #1

4

提示

分别取出 1 1 1 2 2 2 3 3 3 4 4 4 即可。


思路

首先,定义了一些基本变量和数组。n 是一个整数,表示序列的长度,a 是一个长度为 N N N 的整数数组,用于存储序列的元素,dp 是一个长度为 N N N 的长整型数组,用于存储动态规划的状态,表示以 a[i] 结尾的最长上升子序列的长度。

从输入中读取序列的长度 n,然后读取序列的每一个元素,存储在数组 a 中。

然后,进行动态规划。状态转移方程如下:

如果 a [ j ] < a [ i ] a[j] < a[i] a[j]<a[i],则

d p [ i ] = max ⁡ ( d p [ i ] , d p [ j ] + 1 ) dp[i] = \max(dp[i], dp[j] + 1) dp[i]=max(dp[i],dp[j]+1)

其中, d p [ i ] dp[i] dp[i] 表示以 a [ i ] a[i] a[i] 结尾的最长上升子序列的长度, a a a 是输入的序列, i i i j j j 是序列的索引。这个状态转移方程的意义是,如果 a [ j ] a[j] a[j] 可以接在 a [ i ] a[i] a[i] 前面,那么更新以 a [ i ] a[i] a[i] 结尾的最长上升子序列的长度。

dp[i] 表示以 a[i] 结尾的最长上升子序列的长度。对于每一个 i,初始化 dp[i] 1 1 1,然后遍历 j 1 1 1i - 1,如果 a[j] 小于 a[i],则更新 dp[i]max(dp[i], dp[j] + 1),这表示如果 a[j] 可以接在 a[i] 前面,那么更新以 a[i] 结尾的最长上升子序列的长度。

动态规划过程结束后,寻找 dp 数组中的最大值,这就是序列的最长上升子序列的长度,输出这个值。


AC代码

#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;

const int N = 1e6 + 7;

int n;
int a[N];
ll dp[N];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		dp[i] = 1;
		for (int j = 1; j < i; j++) {
			if (a[j] < a[i]) {
				dp[i] = max(dp[i], dp[j] + 1);
			}
		}
	}
	ll ans = 0;
	for (int i = 1; i <= n; i++) {
		ans = max(ans, dp[i]);
	}
	cout << ans << "\n";
	return 0;
}