Informática Técnicas de programação - Maior Subsequencia Crescente (LIS) - Método 2 de calcular LIS (DP)

Aula por Caique Paiva

Conhecimento prévio necessário:

O intuito dessa aula é resolver o seguinte problema:

Dado uma sequência a_1, a_2, \cdots , a_n, ache o tamanho da maior subsequência crescente de a.

Por exemplo, na sequência  \{ 1, 3, 7 ,2, 5, 4, 10 \} , a maior subsequência crescente é  \{ 1, 3, 5, 10 \} , logo a resposta é 4. Vamos fazer N \le 2 \cdot 10^5 e a_i \le 10^9 para todo i.

Vamos achar um algoritmo que roda em O(n \log ) com uma dp, e a melhor parte desse algoritmo é que podemos aplicar a mesma ideia para outros problemas.

A dp será a seguinte: Defina como dp(x) para ser a maior subsequênca crescente no qual último valor dessa subsequência é x. A ideia do problema é começar a calcular essa dp no intervalo [1, 1], e depois passar para o intervalo [1, 2], depois para o intervalo [1, 3], e ir até o intervalo [1, n].

Com isso, suponha que calculamos essa dp para o intervalo [1, i], então, vamos calcular ela para o intervalo [1, i+1]. Primeiro, veja que o único valor da dp que nós vamos mudar é o de dp[a_{i+1}], pois as outras subsequências (Ou seja, as que não terminam em a_{i+1}) estão inclusas em [1, i]. Com isso, vamos atualizar dp[a_{i+1}].

Com isso, para toda subsequência crescente que acaba em um número x \le a_{i+1}, então, se incluirmos o a_{i+1} nela, conseguimos uma subsequência crescente que acaba em a_{i+1}, e ela vai ter tamanho máximo de dp(x)+1! Além disso, se x \ge a_{i+1}, não podemos incluir a_{i+1} na subsequência, ou seja, não contamos esse caso na dp. Portanto, a fórmula para calcular essa transição vai ser

dp(a_{i+1}) = max_{1 \le x \le a_{i+1}} (dp(x)) + 1

Porém, temos dois problemas nessa solução, mas eles são tranquilos de resolver:

  1. Primeiro é o fato de que a_i \le 10^9. Isso é um problema, pois precisamos criar um vetor para dp de tamanho max (a_i), que pode ser igual a 10^9, resultando num MLE. Para resolver isso, basta fazer uma compressão de coordenadas, fazendo a_i \le N.
  2. O segundo problema é que, para calcular max_{1 \le x \le a_{i+1}} dp(x), fazemos um algoritmo em O(a_{i+1}), ou seja, calcular essa dp é em O(N^2) . Para resolver isso, basta ver que, se fizermos uma segtree de máximo, e então, em cada passo, calculamos a query de [1, a_{i+1}], e fazemos o update de a_{i+1} como descrito acima, e então, a transição fica O(log n).

Com isso, achamos o algoritmo em O(Nlog).

Clique aqui para ver o código