Difícil · PD

Subsecuencia creciente más larga

Una subsecuencia salta elementos pero respeta su orden — de 10 9 2 5 3 7, la cadena 2 3 7 es legal y 3 2 no. Define best[i] como la cadena creciente más larga que termina justo en la posición i, y cada elemento solo pregunta: ¿quién antes que yo era menor, y cuánto medía su mejor cadena?

En una frase<code>best[i] = 1 + max(best[j])</code> sobre j &lt; i con <code>a[j] &lt; a[i]</code> (o 1); la respuesta es el máximo sobre todos los i — un doble bucle O(n²).

El problema

Entrada: La primera línea es n (1 ≤ n ≤ 500). La segunda trae n enteros (−1000 a 1000).

Salida: La longitud de la subsecuencia estrictamente creciente más larga.

Ejemplos:

EntradaSalida
8
10 9 2 5 3 7 101 18
4
4
2 2 2 2
1

En el primer ejemplo la respuesta 4 es la cadena 2, 3, 7, 18 (o 2, 5, 7, 18). El segundo ejemplo es el detector de rigor: iguales vecinos no encadenan, así que cuatro 2 dan longitud 1, no 4.

Tu solución

main.cpp — tu solución
Entrada personalizada para «Ejecutar mi entrada»

La corrección corre en tu navegador contra pruebas ocultas. Un bucle infinito se corta a los 3 segundos — aun así, asegúrate de que todo bucle tenga salida. Tu código se guarda solo mientras escribes.

¿Atascado? Pistas

Pista 1 — Fija la posición final

«La subsecuencia creciente más larga» resbala hasta que fijas dónde termina. best[i] = la cadena más larga que termina justo en i convierte la meta vaga en n subpreguntas precisas — y la respuesta real es simplemente la mayor de todas. Este movimiento de «lo mejor que termina aquí» es el mismo que rompió Suma máxima de subarray.

Pista 2 — Cada elemento arranca en 1

Antes de cualquier comparación, best[i] = 1: el elemento solo ya es una cadena de longitud uno. Luego mejórala con el mejor predecesor menor. Dos bugs clásicos rompen esta base: olvidar asignarla (leer el global sin inicializar — este motor no pone arrays a cero) o arrancar best[i] en 0, con lo que la cadena nunca cuenta su propio último elemento.

Pista 3 — Estricto significa estricto

La comparación es a[j] < a[i], no <=. Con vecinos iguales permitidos, el test todo-igual devuelve n en vez de 1 — el segundo ejemplo del juez existe justo para cazar ese carácter.

Solución (¡inténtalo primero!)
#include <iostream>
using namespace std;

int a[505], best[505];

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

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

    cout << ans << endl;
    return 0;
}

El doble bucle O(n²) es la versión honesta; existe una mejora O(n log n) que guarda, para cada longitud, la cola más pequeña posible — y necesita Búsqueda binaria para colocar nuevos elementos. Entre ambas, fíjate en lo que no cambia: la definición de best[i]. Los algoritmos mejores rara vez sustituyen el significado de una PD; hacen que el mismo significado se consulte más barato.

Análisis profundo

Por qué funciona

Fija el final: best[i] = la cadena estrictamente creciente más larga que termina justo en i. Cada i escanea entonces todo su pasado buscando elementos menores con las mejores cadenas — O(n) predecesores cada uno, O(n²) total — y la respuesta global es el máximo sobre todos los finales. El encuadre «lo mejor que termina aquí» es lo que hace computable la meta vaga.

El coste

O(n²) de tiempo, O(n) de espacio — 500² = 250.000 comparaciones, instantáneo. La mejora O(n log n) guarda la cola mínima por longitud e inserta con búsqueda binaria, pero la definición de la PD sobrevive intacta.

Dónde falla

  • a[j] <= a[i] en vez de < estricto: el test todo-igual devuelve n en vez de 1 — un carácter, una respuesta errónea.
  • Olvidar best[i] = 1 antes del escaneo: en este motor el global es basura, y hasta en compiladores reales el caso base es lo que permite que una cadena empiece en cualquier parte.
  • Informar best[n-1] en vez del máximo sobre todos los i: la cadena más larga puede terminar donde sea (mira el test descendente, donde termina en la posición 0).

Siguiente paso

La misma PD de «terminar aquí» en una dimensión y sin parejas: Suma máxima de subarray. La mejora de velocidad necesita Búsqueda binaria sobre las colas de las cadenas.

Dos cosas para recordar

  • best[i] = 1 + la mejor entre predecesores menores; respuesta = máximo sobre finales.
  • < estricto frente a <= es toda la diferencia entre distintos y no-decrecientes.
🐞 Esta versión falla: ábrela y pruébala

Usa a[j] <= a[i] — los iguales encadenan, así que todo-2 da 4 en vez de 1

#include <iostream>
using namespace std;

int a[505], best[505];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    int ans = 0;
    for (int i = 0; i < n; i++) {
        best[i] = 1;
        for (int j = 0; j < i; j++) {
            if (a[j] <= a[i] && best[j] + 1 > best[i]) {   // bug: not strict
                best[i] = best[j] + 1;
            }
        }
        if (best[i] > ans) ans = best[i];
    }
    cout << ans << endl;
    return 0;
}