diff --git a/08. Recursion/8.1. Triple step.md b/08. Recursion/8.1. Triple step.md new file mode 100644 index 0000000..b55f438 --- /dev/null +++ b/08. Recursion/8.1. Triple step.md @@ -0,0 +1,81 @@ +# 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. + +```cpp +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: + +```cpp +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. diff --git a/08. Recursion/8.3. magic_index.md b/08. Recursion/8.3. Magic index.md similarity index 100% rename from 08. Recursion/8.3. magic_index.md rename to 08. Recursion/8.3. Magic index.md diff --git a/08. Recursion/README.md b/08. Recursion/README.md index 91ad37b..89a3c4d 100644 --- a/08. Recursion/README.md +++ b/08. Recursion/README.md @@ -1,6 +1,6 @@ # Chapter 8 Recursion and dynamic programming -Questions p145, solutions p353 +Questions p146, solutions p353 You can know a problem is recursive when it can be built off of subproblems. If a program says: compute the nth, or the first n, or all..., it might be recursive.