Bird
Raised Fist0
DSA Cprogramming~10 mins

Overlapping Subproblems and Optimal Substructure in DSA C - Execution Trace

Choose your learning style10 modes available
Concept Flow - Overlapping Subproblems and Optimal Substructure
Problem
→Break into subproblems
↓
Check if subproblem solved?
Yes→Use stored solution
No↓
Compute subproblem
↓
Store solution
↓
Combine subproblems for final answer
The problem is divided into smaller subproblems. If a subproblem was solved before, reuse its answer. Otherwise, solve it, store the result, and combine all to get the final solution.
Execution Sample
DSA C
int fib(int n) {
  if (n <= 1) return n;
  return fib(n-1) + fib(n-2);
}
This code calculates Fibonacci numbers using recursion, showing overlapping subproblems as fib(n-1) and fib(n-2) are computed multiple times.
Execution Table
StepOperationSubproblem ComputedStored SolutionsVisual State
1Call fib(4)fib(4){}fib(4) called, no stored solutions
2Call fib(3)fib(3){}fib(3) called inside fib(4)
3Call fib(2)fib(2){}fib(2) called inside fib(3)
4Call fib(1)fib(1){}fib(1) base case returns 1
5Return fib(1) = 1fib(1){fib(1)=1}Store fib(1)=1
6Call fib(0)fib(0){fib(1)=1}fib(0) base case returns 0
7Return fib(0) = 0fib(0){fib(1)=1, fib(0)=0}Store fib(0)=0
8Return fib(2) = 1fib(2){fib(1)=1, fib(0)=0}fib(2) = fib(1)+fib(0) = 1+0=1
9Call fib(1)fib(1){fib(1)=1, fib(0)=0}fib(1) found in stored solutions, reuse 1
10Return fib(3) = 2fib(3){fib(1)=1, fib(0)=0, fib(2)=1}fib(3) = fib(2)+fib(1) = 1+1=2
11Call fib(2)fib(2){fib(1)=1, fib(0)=0, fib(2)=1}fib(2) found in stored solutions, reuse 1
12Return fib(4) = 3fib(4){fib(1)=1, fib(0)=0, fib(2)=1, fib(3)=2}fib(4) = fib(3)+fib(2) = 2+1=3
13End-{fib(0)=0, fib(1)=1, fib(2)=1, fib(3)=2, fib(4)=3}Final result fib(4)=3
💡 fib(4) computed using stored subproblems, recursion ends
Variable Tracker
VariableStartAfter Step 5After Step 7After Step 8After Step 10After Step 12Final
fib(0)undefinedundefined00000
fib(1)undefined111111
fib(2)undefinedundefinedundefined1111
fib(3)undefinedundefinedundefinedundefined222
fib(4)undefinedundefinedundefinedundefinedundefined33
Key Moments - 3 Insights
Why do we compute fib(2) multiple times in the naive recursion?
Because fib(2) is called separately from fib(3) and fib(4) without storing its result, as seen in steps 3 and 11. This shows overlapping subproblems.
How does storing solutions help avoid repeated work?
Stored solutions like fib(1)=1 and fib(0)=0 are reused in steps 9 and 11, preventing recomputation and saving time.
What does optimal substructure mean in this example?
The solution to fib(4) depends on solutions to fib(3) and fib(2), which are smaller subproblems. Combining these gives the optimal answer, as shown in step 12.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of fib(2) after step 8?
A2
B0
C1
DUndefined
💡 Hint
Check the 'Stored Solutions' column at step 8 where fib(2) is computed as 1.
At which step does the recursion reuse a stored solution for fib(1)?
AStep 4
BStep 9
CStep 6
DStep 12
💡 Hint
Look for when fib(1) is found in stored solutions instead of recomputed.
If we add memoization to the code, how would the number of fib(2) computations change?
AIt would reduce to one
BIt would stay the same
CIt would increase
DIt would become zero
💡 Hint
Refer to variable_tracker and execution_table showing repeated fib(2) calls without memoization.
Concept Snapshot
Overlapping Subproblems and Optimal Substructure:
- Break problem into smaller subproblems
- Reuse solutions of repeated subproblems (memoization)
- Combine subproblem solutions for final answer
- Enables efficient dynamic programming
- Example: Fibonacci numbers computed once each
Full Transcript
This concept shows how some problems can be split into smaller parts that repeat. Instead of solving the same part many times, we save the answer once and reuse it. This saves time and makes the solution faster. The Fibonacci example shows how fib(2) is computed multiple times without saving, but with storing results, each subproblem is solved once. The problem also has optimal substructure, meaning the final answer is made by combining answers of smaller parts. This is the base of dynamic programming.

Practice

(1/5)
1. Which of the following best describes the concept of overlapping subproblems in dynamic programming?
easy
A. The problem requires sorting elements before solving.
B. The problem has a unique solution without repeating any subproblems.
C. The problem can be broken down into smaller subproblems which are solved multiple times.
D. The problem can be solved only using recursion without optimization.

Solution

  1. Step 1: Understand overlapping subproblems

    Overlapping subproblems means the same smaller problems appear multiple times during the solution process.
  2. Step 2: Match with options

    The problem can be broken down into smaller subproblems which are solved multiple times. correctly states that subproblems are solved multiple times, which is the key idea behind overlapping subproblems.
  3. Final Answer:

    The problem can be broken down into smaller subproblems which are solved multiple times. -> Option C
  4. Quick Check:

    Overlapping subproblems = repeated smaller problems [OK]
Hint: Look for repeated smaller problems solved multiple times [OK]
Common Mistakes:
  • Confusing overlapping subproblems with unique subproblems
  • Thinking sorting is required for overlapping subproblems
  • Assuming recursion alone solves overlapping subproblems
2. Which of the following is the correct syntax to declare a memoization array for storing results of subproblems in C?
easy
A. int memo[] = 0;
B. int memo = new int[1000];
C. int memo(1000);
D. int memo[1000] = {0};

Solution

  1. Step 1: Understand C array declaration

    In C, arrays are declared with fixed size and can be initialized with {0} to set all elements to zero.
  2. Step 2: Check options for valid C syntax

    int memo[1000] = {0}; uses correct C syntax: int memo[1000] = {0}; initializes an array of 1000 integers to zero.
  3. Final Answer:

    int memo[1000] = {0}; -> Option D
  4. Quick Check:

    C array declaration with initialization = int memo[1000] = {0}; [OK]
Hint: Use fixed size array with braces to initialize in C [OK]
Common Mistakes:
  • Using C++ style vector or new keyword in C
  • Declaring arrays without size
  • Incorrect initialization syntax
3. Consider the following C code for Fibonacci with memoization:
int memo[100] = {0};

int fib(int n) {
    if (n <= 1) return n;
    if (memo[n] != 0) return memo[n];
    memo[n] = fib(n-1) + fib(n-2);
    return memo[n];
}

int main() {
    printf("%d", fib(5));
    return 0;
}
What is the output when this code runs?
medium
A. 0
B. 5
C. 3
D. 8

Solution

  1. Step 1: Trace fib(5) calls with memoization

    fib(5) calls fib(4) + fib(3). fib(4) calls fib(3) + fib(2), and so on. Base cases fib(0)=0, fib(1)=1.
  2. Step 2: Calculate fib(5)

    fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=fib(4)+fib(3)=3+2=5.
  3. Final Answer:

    5 -> Option B
  4. Quick Check:

    Fibonacci(5) = 5 [OK]
Hint: Recall Fibonacci sequence: 0,1,1,2,3,5... [OK]
Common Mistakes:
  • Confusing index with Fibonacci value
  • Ignoring memoization effect
  • Miscomputing base cases
4. The following code attempts to compute the nth Fibonacci number using memoization but has a bug:
int memo[100] = {0};

int fib(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;
    if (memo[n] != 0) return memo[n];
    memo[n] = fib(n-1) + fib(n-2);
    return memo[n];
}

int main() {
    printf("%d", fib(0));
    return 0;
}
What is the bug in this code?
medium
A. fib(0) returns 0 but memo[0] is never set, causing repeated calls.
B. The memo array is not initialized to zero.
C. The base case for fib(1) is missing.
D. The memoization check uses wrong index.

Solution

  1. Step 1: Analyze base cases and memo usage

    fib(0) returns 0 but does not store it in memo[0]. So memo[0] remains 0, which is the default value.
  2. Step 2: Understand effect on memoization

    Because the base case results are not stored in memo[n], fib(0) (and fib(1)) will be called repeatedly in the recursion tree for larger n, instead of returning a cached value.
  3. Final Answer:

    fib(0) returns 0 but memo[0] is never set, causing repeated calls. -> Option A
  4. Quick Check:

    Base case not stored in memo causes repeated calls [OK]
Hint: Store base case results in memo to avoid repeated calls [OK]
Common Mistakes:
  • Assuming memo array auto-initializes for base cases
  • Missing base case for fib(1)
  • Incorrect memo check condition
5. You want to find the minimum cost to reach the top of stairs where each step has a cost. You can climb 1 or 2 steps at a time. The cost array is cost[] = {10, 15, 20}. Using dynamic programming with overlapping subproblems and optimal substructure, what is the minimum cost to reach the top (beyond last step)?
hard
A. 15
B. 20
C. 25
D. 10

Solution

  1. Step 1: Define subproblems and recurrence

    Minimum cost to reach step i: dp[i] = cost[i] + min(dp[i-1], dp[i-2]). dp[0]=10, dp[1]=15.
  2. Step 2: Calculate minimum cost step by step

    dp[2] = 20 + min(10,15) = 30. To reach top (beyond last step), min(dp[1], dp[2]) = min(15,30) = 15.
  3. Final Answer:

    15 -> Option A
  4. Quick Check:

    Minimum cost to top = 15 [OK]
Hint: Use min cost of last two steps for final answer [OK]
Common Mistakes:
  • Adding all costs instead of choosing min path
  • Ignoring that you can start from step 0 or 1
  • Confusing cost to step vs cost beyond last step
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard