题解:AcWing 895 最长上升子序列

【题目来源】

AcWing:895 最长上升子序列 - AcWing题库

【题目描述】

给定一个长度为 \(N\) 的数列,求数值严格单调递增的子序列的长度最长是多少。

【输入】

第一行包含整数 \(N\)

第二行包含 \(N\) 个整数,表示完整序列。

【输出】

输出一个整数,表示最大长度。

【输入样例】

7
3 1 2 1 8 5 6

【输出样例】

4

【核心思想】

  1. 问题分析:给定长度为 \(N\) 的数列,求数值严格单调递增的子序列的最大长度。这是一个经典的线性动态规划问题,需要找到以每个元素结尾的最长上升子序列。

  2. 算法选择

    • 线性 DP\(f[i]\) 表示以 \(a[i]\) 结尾的最长上升子序列的长度
    • 暴力解法:对于每个位置 \(i\),枚举前面所有位置 \(j < i\),若 \(a[j] < a[i]\),则可以接在 \(a[i]\) 后面
  3. 关键步骤

    • 初始化\(f[i] = 1\),每个元素自身构成长度为 \(1\) 的子序列
    • 状态转移:对于每个 \(i\),枚举 \(j\)\(1\)\(i-1\)
      • \(a[j] < a[i]\),则 \(f[i] = \max(f[i], f[j] + 1)\)
    • 答案\(\max(f[1], f[2], \ldots, f[N])\),取所有位置的最大值
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2)\),双重循环枚举
    • 空间复杂度:\(O(N)\),一维 DP 数组
  5. 最长上升子序列(LIS)的核心思想

    • 状态定义\(f[i]\) 表示以第 \(i\) 个元素结尾的 LIS 长度
    • 状态转移:对于每个 \(i\),找前面所有比 \(a[i]\) 小的元素 \(j\),取 \(f[j]\) 的最大值加 \(1\)
    • 最优子结构:以 \(a[i]\) 结尾的 LIS 可以由前面某个 \(a[j]\) 结尾的 LIS 扩展而来
    • 优化方向:可以使用二分优化至 \(O(N \log N)\),用数组维护每个长度的最小结尾元素
    • 适用于序列分析、数据比较等场景

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 1005; // 定义常量N,表示数组的最大长度
int n; // 输入的数组长度
int a[N]; // 存储输入的数组
int f[N]; // f[i]表示以a[i]结尾的最长上升子序列的长度

int main()
{
    cin >> n; // 输入数组的长度
    for (int i = 1; i <= n; i++) cin >> a[i]; // 输入数组元素

    // 动态规划求解最长上升子序列
    for (int i = 1; i <= n; i++)
    {
        f[i] = 1; // 初始化f[i],至少包含a[i]一个数,所以长度为1

        // 遍历a[i]之前的所有元素,寻找可以接在a[i]前面的最长上升子序列
        for (int j = 1; j < i; j++)
            if (a[j] < a[i]) // 如果a[j] < a[i],说明a[j]可以接在a[i]前面
                f[i] = max(f[i], f[j] + 1); // 更新f[i],取f[j]+1和当前f[i]的最大值
    }

    int res = 0; // 用于存储最终的最长上升子序列的长度
    for (int i = 1; i <= n; i++)
        res = max(res, f[i]); // 遍历f数组,找到最大值

    cout << res << endl; // 输出结果
    return 0;
}

【运行结果】

7
3 1 2 1 8 5 6
4
posted @ 2026-06-14 10:37  团爸讲算法  阅读(5)  评论(0)    收藏  举报