{ }codelper
动态规划嵌套循环P0089

最长上升子序列

给出由 n 个整数组成的数列 a₁ … aₙ。
从中选出若干个(保持原来的顺序,中间可以跳过)组成的数列如果严格递增(后面的数总比前面的大,不能相等),输出这种数列的最大长度。
只选一个数的数列也算递增数列。
例如在 3 1 4 1 5 9 2 6 中可以选 1 4 5 9(或 3 4 5 6 等),最长长度为 4。
第一行给出 n,第二行给出用空格隔开的 a₁ … aₙ。

输入
1 ≤ n ≤ 2,000,-10⁹ ≤ aᵢ ≤ 10⁹
输出
最长严格上升子序列的长度(一个整数)
样例 1 · stdin
8
3 1 4 1 5 9 2 6
stdout
4
样例 2 · stdin
5
2 2 2 2 2
stdout
1
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

设 length[i] 为「以 a[i] 结尾的最长上升序列的长度」,它等于前面 j < i 中满足 a[j] < a[i] 的 length[j] + 1 的最大值(没有则为 1)。
双重循环约 n²/2 = 两百万次,8 种语言都很充裕(在 Craykon 约五千万执行步数的上限之内)。
三个陷阱:把相等的值也接上的 <= 不是「严格递增」。
答案不是最后一格 length[n-1],而是所有格中的最大值——最长的序列不一定在最后结束。
另外只数连续的区间会漏掉跳过元素的序列。
求最大值的方式各语言不同:Python max(length),C# LINQ length.Max(),C++ *std::max_element(…),JavaScript/TypeScript Math.max(...length)(展开参数的个数有上限,超过几十万格时用循环更安全)。
Java 和 C 另外记一个 best(Java 也可以用 Arrays.stream(length).max().getAsInt())。
初始填充也不同:Python [1] * n,JS new Array(n).fill(1),C++ vector<int>(n, 1);Java、C#、C 默认填 0,所以要自己写 length[i] = 1。
Craykon 用 新列表(n, 1) 创建,并另外记一个 best。

Pythonmain.py · 13 行
import sys data = sys.stdin.read().split()n = int(data[0])a = list(map(int, data[1:1 + n])) # length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이length = [1] * nfor i in range(n):    for j in range(i):        if a[j] < a[i] and length[j] + 1 > length[i]:            length[i] = length[j] + 1print(max(length))
JavaScriptmain.js · 10 行
const [n, ...a] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이const length = new Array(n).fill(1);for (let i = 0; i < n; i++) {  for (let j = 0; j < i; j++) {    if (a[j] < a[i] && length[j] + 1 > length[i]) length[i] = length[j] + 1;  }}console.log(Math.max(...length));
TypeScriptmain.ts · 11 行
import { readFileSync } from "node:fs";const [n, ...a]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이const length: number[] = new Array(n).fill(1);for (let i = 0; i < n; i++) {  for (let j = 0; j < i; j++) {    if (a[j] < a[i] && length[j] + 1 > length[i]) length[i] = length[j] + 1;  }}console.log(Math.max(...length));
JavaMain.java · 26 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        StringTokenizer st = new StringTokenizer(br.readLine());        int[] a = new int[n];        for (int i = 0; i < n; i++) a[i] = Integer.parseInt(st.nextToken());         // length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이        int[] length = new int[n];        int best = 0;        for (int i = 0; i < n; i++) {            length[i] = 1;            for (int j = 0; j < i; j++) {                if (a[j] < a[i] && length[j] + 1 > length[i]) length[i] = length[j] + 1;            }            best = Math.max(best, length[i]);        }        System.out.println(best);    }}
C#Main.cs · 23 行
using System;using System.Linq; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine());        int[] a = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();         // length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이        int[] length = new int[n];        for (int i = 0; i < n; i++)        {            length[i] = 1;            for (int j = 0; j < i; j++)            {                if (a[j] < a[i] && length[j] + 1 > length[i]) length[i] = length[j] + 1;            }        }        Console.WriteLine(length.Max());    }}
C++main.cpp · 19 行
#include <algorithm>#include <iostream>#include <vector> int main() {    int n;    std::cin >> n;    std::vector<int> a(n);    for (int& x : a) std::cin >> x;     // length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이    std::vector<int> length(n, 1);    for (int i = 0; i < n; i++) {        for (int j = 0; j < i; j++) {            if (a[j] < a[i] && length[j] + 1 > length[i]) length[i] = length[j] + 1;        }    }    std::cout << *std::max_element(length.begin(), length.end()) << '\n';}
Cmain.c · 22 行
#include <stdio.h> #define MAXN 2000 int main(void) {    static int a[MAXN], length[MAXN];    int n;    scanf("%d", &n);    for (int i = 0; i < n; i++) scanf("%d", &a[i]);     /* length[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이 */    int best = 0;    for (int i = 0; i < n; i++) {        length[i] = 1;        for (int j = 0; j < i; j++) {            if (a[j] < a[i] && length[j] + 1 > length[i]) length[i] = length[j] + 1;        }        if (length[i] > best) best = length[i];    }    printf("%d\n", best);    return 0;}
Craykonmain.cray · 20 行
n = 控制台.读行().转数字();a = 控制台.读数字();// len[i] = a[i]로 끝나는 가장 긴 증가 부분 수열의 길이len = 新列表(n, 1);best = 1;循环 (i = 1; i < n; i++){    循环 (j = 0; j < i; j++)    {        如果 (a[j] < a[i] && len[j] + 1 > len[i])        {            len[i] = len[j] + 1;        }    }    如果 (len[i] > best)    {        best = len[i];    }}控制台.输出(best);