{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

length[i] = 'a[i]로 끝나는 가장 긴 증가 수열의 길이'로 두면, 앞의 j < i 가운데 a[j] < a[i]인 것의 length[j] + 1 중 가장 큰 값이에요(없으면 1).
두 겹 반복이 n²/2 = 약 200만 번이라 8개 언어 모두 넉넉해요(크레이콘 실행 걸음 한도 약 5천만 안).
함정 셋: 같은 값도 이어 붙이는 <=는 '엄격히 증가'가 아니에요.
답은 마지막 칸 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을 직접 써요.
크레이콘은 새목록(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);