ctci/08. Recursion/8.1. Triple step.md

2.5 KiB

8.1. Triple step

A child is running up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. Implement a method to count how many possible ways the child can run up the stairs.

I'm assuming the kid can't go back. Also, the result for n must be the addition/multiplication of the result for n-1.

  • Example: 0 1 2
    • 1x: 1, 012
    • 2x: 1, 02
    • 3x: -
    • total: 2
  • Example: 0 1 2 3
    • 1x: 1, 0123
    • 2x: 2, 023, 013
    • 3x: 1, 03
    • total: 4
  • Example: 0 1 2 3 4 5
    • 1x: 1, 01234
    • 2x: 4, 0245, 0135, 01345, 02345
    • 3x: 5, 035, 0345, 0145, 0125, 025
    • total: 10
  • Example: 0 1 2 3 4 5 6
    • 1x: 1, 0123456
    • 2x: 6, 0246, 02456, 02356, 01356, 01246, 012346
    • 3x: 8, 036, 0346, 0356, 03456, 01456, 0146, 01256, 0256
    • total, 22
  • Example: 0 1 2 3 4 5 6 7
    • 1x: 1, 0123456
    • 2x: x, 0134567, 01357, 013457, 013467, 0234567, 02357, 023467, 023457, 024567, 02457, 02467, ?

Hints

Approach this from the top down. What is the very last hop the child made?

Either a hop from n-1, n-2, or n-3.

If we knew the number of paths to each of the steps before step 100, could we compute the number of steps to 100?

That's the point of dynamic programming but I don't see it.

We can compute the number of steps to 100 by the number of steps to 99, 98, and 97. This corresponds to the child hopping 1, 2, or 3 steps at the end. Do we add those or multiply them?That is: Is it f(100) = f(99) + f(98) + f(97) or f(100) = f(99) * f(98) * f(97)?

Add, either one path or the other is taken. Not all. Multiplying one path with another would signify taking one path and then taking the other.

Solution

countWays(n) = countWays(n-1) + countWays(n-2) + countWays(n-3). What about base cases? We could say that if n==0, return 1.

int countWays(int n) {
    if (n < 0) {
        return 0;
    } else if (n == 0) {
        return 1;
    } else {
        return countWays(n-1) + countWays(n-2)+ countWays(n-3);
    }
}

But this is very inefficient, O(3n). Memoization:

int countWays(int n) {
    int[] memo = new int[n+ 1];
    Arrays.fill(memo, -1);
    return countWays(n, memo);
}

int countWays(int n, int[] memo) {

    if (n < 0) return 0;
    else if (n == 0) return 1;
    else if (memo[n] > -1) return memo[n];

    memo[n] = countWays(n-1, memo) + countWays(n-2, memo) + countWays(n-3, memo);
    return memo[n];
}

First fill the memo with -1s. When we start with our n, n=4 for example, countWays(3), memo is empty so it's created. Memo is created from the bottom up.