Grade 11 · Dynamic programmingCitește în română

Longest strictly increasing subsequence

Original: Subșirul strict crescător de lungime maximă

O(n²)C++1771 lines1 example

The idea

From a sequence of n numbers we choose a strictly increasing subsequence (elements in their order in the sequence, not necessarily adjacent) that is as long as possible. L[i] is the length of the longest increasing subsequence starting with a[i]. We compute from right to left: L[i] = 1 + max{ L[j] : j > i, a[j] > a[i] } (or 1 if there is no such j). The answer is the largest L[i]. To rebuild it we start from that i and always look for the first j after it with a[j] > a[i] and L[j] = L[i] - 1. The length and a longest subsequence are printed.

Original description (Romanian)

Dintr-un șir de n numere alegem un subșir (elemente în ordinea din șir, nu neapărat alăturate) strict crescător și cât mai lung. L[i] este lungimea celui mai lung subșir crescător care începe cu a[i]. Calculăm de la dreapta la stânga: L[i] = 1 + max{ L[j] : j > i, a[j] > a[i] } (sau 1 dacă nu există un astfel de j). Răspunsul este cel mai mare L[i]. Pentru reconstituire pornim de la acel i și căutăm mereu primul j de după el cu a[j] > a[i] și L[j] = L[i] - 1. Se afișează lungimea și un subșir maximal.

Complexity
O(n²) timp, O(n) memorie
Grade
Grade 11 (Clasa a XI-a)

Examples

Example 1

Input

8
5 1 7 3 4 9 2 8

Expected output

4
1 3 4 9

The code

Comments are in Romanian, as in the original collection. Edit freely: you can reset at any time.

subsir-crescator-maximal.cpp
/*
 * Titlu: Subșirul strict crescător de lungime maximă
 * Clasa: 11
 * Capitol: Programare dinamică
 * Descriere: Dintr-un șir de n numere alegem un subșir (elemente în ordinea din șir, nu
 *            neapărat alăturate) strict crescător și cât mai lung. L[i] este lungimea
 *            celui mai lung subșir crescător care începe cu a[i]. Calculăm de la dreapta
 *            la stânga: L[i] = 1 + max{ L[j] : j > i, a[j] > a[i] } (sau 1 dacă nu există
 *            un astfel de j). Răspunsul este cel mai mare L[i]. Pentru reconstituire pornim
 *            de la acel i și căutăm mereu primul j de după el cu a[j] > a[i] și
 *            L[j] = L[i] - 1. Se afișează lungimea și un subșir maximal.
 * Complexitate: O(n²) timp, O(n) memorie
 *
 * Exemplu
 * Intrare:
 * 8
 * 5 1 7 3 4 9 2 8
 * Ieșire:
 * 4
 * 1 3 4 9
 */
#include <iostream>
using namespace std;

int a[1005], L[1005];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    for (int i = n; i >= 1; i--)
    {
        L[i] = 1;
        for (int j = i + 1; j <= n; j++)
        {
            if (a[j] > a[i] && L[j] + 1 > L[i])
            {
                L[i] = L[j] + 1;
            }
        }
    }

    int start = 1;
    for (int i = 2; i <= n; i++)
    {
        if (L[i] > L[start])
        {
            start = i;
        }
    }
    cout << L[start] << '\n';

    int i = start;
    cout << a[i] << ' ';
    for (int j = i + 1; j <= n; j++)
    {
        if (a[j] > a[i] && L[j] == L[i] - 1)
        {
            cout << a[j] << ' ';
            i = j;
        }
    }
    cout << '\n';
    return 0;
}

Runs on a real g++ (C++17, -O2) through Compiler Explorer. Keep runs short; there's a time limit.