Pular para o conteudo principal

Subindo escadas

Como perceber a recorrencia pelas ultimas duas escolhas e usar isso como porta de entrada para programação dinamica.

Você esta subindo uma escada. Para chegar ao topo com n degraus, pode subir 1 ou 2 degraus por vez.

Retorne quantas formas distintas existem para chegar ao topo.

Exemplo:

Input:  n = 3
Output: 3

As formas sao: 1+1+1, 1+2, 2+1.

O que perceber antes de codar

Erros comuns

Passo 1 — Perguntar de onde o ultimo passo pode ter vindo

Para chegar no degrau n, o ultimo movimento só pode ser:

  • de n - 1 para n
  • de n - 2 para n

Não existe terceira opcao.

Isso já sugere que o número de formas de chegar em n depende dos dois casos anteriores.

Passo 2 — Escrever a versão inicial recursiva

A primeira ideia correta costuma ser:

export function climbStairs(n: number): number {
  if (n <= 2) {
    return n
  }

  return climbStairs(n - 1) + climbStairs(n - 2)
}

Funciona. Mas repete muito trabalho.

Para calcular climbStairs(5), você recalcula climbStairs(3) mais de uma vez, por exemplo.

Passo 3 — Identificar a recorrencia

A relação central e:

ways(n) = ways(n - 1) + ways(n - 2)

Porque toda forma de chegar em n termina vindo de um desses dois degraus.

Passo 4 — Guardar só o que realmente importa

Você não precisa manter a árvore inteira da recursão.

Só precisa saber:

  • quantas formas chegam no degrau anterior
  • quantas formas chegam em dois degraus antes

O resto vai sendo descartado.

O editor interativo precisa de JavaScript. Voce ainda pode ler o desafio e copiar o codigo inicial abaixo.

Codigo inicial

export function climbStairs(n: number): number {
  // sua solução aqui
  return 0
}
solution.ts
Travado? Dicas disponiveis.

Ainda não resolveu?

Ver a solução agora pode reduzir o aprendizado. Vale a pena tentar mais um pouco.

Abrir a solucao de referencia

Sem JavaScript, a solucao de referencia aparece inline em vez de um dialogo.

Solução

Complexidade final

Tempo: O(n)

Espaço: O(1)

Solução 1 — Recursao pura antes de memoization

Boa para enxergar a ideia, ruim para escalar.

export function climbStairs(n: number): number {
  if (n <= 2) {
    return n
  }

  return climbStairs(n - 1) + climbStairs(n - 2)
}

O problema aqui não e a formula. E o retrabalho.

Solução 2 — Programacao dinamica com estado reduzido em espaco O(1)

Agora a mesma recorrencia roda de forma iterativa.

export function climbStairs(n: number): number {
  if (n <= 2) {
    return n
  }

  let twoStepsBefore = 1
  let oneStepBefore = 2

  for (let stair = 3; stair <= n; stair += 1) {
    const current = oneStepBefore + twoStepsBefore
    twoStepsBefore = oneStepBefore
    oneStepBefore = current
  }

  return oneStepBefore
}

Você mantem só o estado necessário para calcular o próximo degrau.

O que dizer na entrevista

Uma explicação curta e boa seria:

Para chegar no degrau n, o ultimo passo só pode ter vindo de n - 1 ou n - 2. Isso gera a recorrencia f(n) = f(n - 1) + f(n - 2). A recursão pura funciona, mas recalcula subproblemas. Entao eu transformo em DP iterativa e mantenho só os dois estados anteriores.

Isso mostra que você:

  • enxergou a recorrencia certa
  • explicou por que a recursão pura desperdiça trabalho
  • conectou DP a uma necessidade concreta

Quando esse padrão aparece de novo

Esse padrão reaparece quando você precisa:

  • contar formas de chegar a um estado
  • montar resposta a partir de poucos estados anteriores
  • trocar recursao repetitiva por programacao dinamica linear
  • reconhecer subproblemas repetidos cedo

O ponto não e decorar “isso parece Fibonacci”.

O ponto e justificar de onde a recorrencia vem.

Proximo desafio Maior substring sem caracteres repetidos Desafio anterior Mesclar duas listas ordenadas

Desafios relacionados

Artigos relacionados