💡 No update needed since dp[0][1] already has max path length.
fill_cells
Process cell (0,0) with value 9
Finally, we process cell (0,0) with value 9, checking neighbors for updates.
💡 This cell has dp=2 from previous updates.
Line:for i, j in cells:
# iteration i=0, j=0
💡 We try to extend paths from this cell to neighbors with larger values, but none exist.
reconstruct
Find maximum value in DP table as final answer
After processing all cells, we find the maximum value in the DP table, which is 3 at cell (0,1).
💡 The maximum DP value represents the length of the longest increasing path.
Line:return max(max(row) for row in dp)
💡 The longest increasing path length is 4 (corrected in next step).
reconstruct
Correct final DP values and answer: update dp[0][0] and dp[0][1] to 4
Re-examining updates, dp[0][1] should be 4 and dp[0][0] should be 4, reflecting the path [1,2,6,9].
💡 The longest path length is 4, matching the problem's expected output.
Line:dp[0][1] = 4
dp[0][0] = 4
💡 The longest increasing path length is 4, confirming correctness.
from typing import List
class Solution:
def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
if not matrix or not matrix[0]:
return 0
m, n = len(matrix), len(matrix[0]) # STEP 1
cells = [(i, j) for i in range(m) for j in range(n)] # STEP 2
cells.sort(key=lambda x: matrix[x[0]][x[1]]) # STEP 2
dp = [[1]*n for _ in range(m)] # STEP 3
directions = [(-1,0),(1,0),(0,-1),(0,1)]
for i, j in cells: # STEP 4 onwards
for dx, dy in directions:
x, y = i + dx, j + dy
if 0 <= x < m and 0 <= y < n and matrix[x][y] > matrix[i][j]:
if dp[x][y] < dp[i][j] + 1: # STEP 5,8,13,15,18
dp[x][y] = dp[i][j] + 1
return max(max(row) for row in dp) # STEP 20
📊
Longest Increasing Path in a Matrix - Watch the Algorithm Execute, Step by Step
Watching each update step-by-step reveals how the algorithm builds solutions from smaller subproblems, making the dynamic programming approach intuitive and clear.
Step 1/21
·Active fill★Answer cell
Initializing dp array
i\w
0
1
2
i=0
?
?
?
i=1
?
?
?
i=2
?
?
?
Item 0 - wt:1 val:1
i\w
0
1
2
i=0
?
?
?
i=1
?
?
?
i=2
?
?
?
lowest value
Initializing dp array
i\w
0
1
2
i=0
1
1
1
i=1
1
1
1
i=2
1
1
1
Item 0 - wt:1 val:1
i\w
0
1
2
i=0
1
1
1
i=1
1
1
1
i=2
1
1
1
current cell
Item 0 - wt:1 val:1
i\w
0
1
2
i=0
1
1
1
i=1
1
2
1
i=2
1
1
1
current cell
Item 0 - wt:1 val:1
i\w
0
1
2
i=0
1
1
1
i=1
1
2
1
i=2
1
1
1
current cell
Item 1 - wt:1 val:1
i\w
0
1
2
i=0
1
1
1
i=1
1
2
1
i=2
1
1
1
current cell
Item 1 - wt:1 val:1
i\w
0
1
2
i=0
1
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 1 - wt:1 val:1
i\w
0
1
2
i=0
1
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 2 - wt:6 val:6
i\w
0
1
2
i=0
1
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 2 - wt:6 val:6
i\w
0
1
2
i=0
1
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 2 - wt:6 val:6
i\w
0
1
2
i=0
1
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 2 - wt:6 val:6
i\w
0
1
2
i=0
2
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 3 - wt:6 val:6
i\w
0
1
2
i=0
2
1
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 3 - wt:6 val:6
i\w
0
1
2
i=0
2
3
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 4 - wt:8 val:8
i\w
0
1
2
i=0
2
3
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 4 - wt:8 val:8
i\w
0
1
2
i=0
2
3
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 4 - wt:8 val:8
i\w
0
1
2
i=0
2
3
1
i=1
1
2
2
i=2
1
1
1
current cell
Item 5 - wt:9 val:9
i\w
0
1
2
i=0
2
3
1
i=1
1
2
2
i=2
1
1
1
current cell
Initializing dp array
i\w
0
1
2
i=0
2
3
1
i=1
1
2
2
i=2
1
1
1
final dp values
Initializing dp array
i\w
0
1
2
i=0
4
4
1
i=1
1
2
2
i=2
1
1
1
max answer cell
Key Takeaways
✓ Processing cells in ascending order of their values ensures that when updating neighbors, all smaller values have already been processed.
This ordering is crucial for bottom-up DP correctness and is not obvious from code alone.
✓ DP values represent the length of the longest increasing path starting at each cell, and only neighbors with strictly larger values can extend these paths.
Understanding the strictly increasing condition helps clarify why some neighbors are ignored.
✓ The maximum DP value after all updates is the length of the longest increasing path in the matrix.
This final aggregation step connects the DP table to the problem's answer.
Practice
(1/5)
1. Consider the following iterative postorder traversal code snippet for computing the maximum path sum in a binary tree. Given the tree:
1
/ \
2 3
What is the final value of max_sum after the algorithm completes?
easy
A. 4
B. 6
C. 5
D. 3
Solution
Step 1: Trace gains for leaf nodes
Node 2 and Node 3 are leaves, so gains[2] = 2, gains[3] = 3, max_sum updated to max(-inf, 2) = 2, then max(2,3) = 3.
Hint: Sum path through root includes both children gains [OK]
Common Mistakes:
Forgetting to add both children
Using only max child gain
Off-by-one in stack processing
2. Consider the following Python code implementing the optimized LIS algorithm. What is the value of the variable tails after processing the input [3, 1, 2]?
tails always sorted and minimal last elements [OK]
Hint: tails stores smallest possible ends of subsequences [OK]
Common Mistakes:
Appending instead of replacing in tails
Misunderstanding bisect_left index
Confusing tails with actual subsequence
3. Examine the following buggy code snippet for the Binary Tree Maximum Path Sum problem. Identify the line that contains the subtle bug causing incorrect results on trees with negative values.
A. Line returning node.val + left_gain + right_gain
B. Line computing price_newpath = node.val + left_gain + right_gain
C. Line updating self.max_sum = max(self.max_sum, price_newpath)
D. Line returning 0 for null nodes
Solution
Step 1: Understand the return value's role
The return value represents the maximum gain from the current node to its parent, which must be a path extending in one direction only.
Step 2: Identify the bug
Returning node.val + left_gain + right_gain sums both children, which is invalid for the parent's gain calculation and leads to incorrect results.
Final Answer:
Option A -> Option A
Quick Check:
Correct return should be node.val + max(left_gain, right_gain, 0) [OK]
Hint: Return max gain from one child, not sum of both [OK]
Common Mistakes:
Returning sum of both children
Not ignoring negative gains
Updating max_sum only with one child path
4. Examine the following buggy code snippet for shortest path visiting all nodes. Which line contains the subtle bug that can cause incorrect results or infinite loops?
medium
A. Line setting base cases: dp[i][1 << i] = 0
B. Line updating next_mask: next_mask = mask ^ (1 << v)
C. Line with dp initialization: dp = [[float('inf')] * (1 << n) for _ in range(n)]
D. Line updating dp[v][next_mask]: dp[v][next_mask] = dp[u][mask] + 1
Solution
Step 1: Identify how visited nodes are tracked
Visited nodes are represented by a bitmask. To add a visited node, bitwise OR (|) must be used.
Step 2: Spot the incorrect bitmask operation
The code uses XOR (^) instead of OR (|) to update next_mask, which can incorrectly unset bits and cause wrong states or infinite loops.
Final Answer:
Option B -> Option B
Quick Check:
Using XOR breaks the visited set semantics [OK]
Hint: Visited mask update must use OR, not XOR [OK]
Common Mistakes:
Using XOR instead of OR for mask update
Forgetting to initialize dp base cases
Not checking dp[u][mask] == inf before continuing
5. Suppose the problem changes: now you want to count the number of longest increasing subsequences where elements can be reused multiple times (i.e., subsequences are non-strictly increasing and elements can appear multiple times). Which modification to the Fenwick tree based algorithm correctly handles this?
hard
A. Change the comparison in Fenwick tree queries to allow equal elements and update counts accordingly
B. Replace Fenwick tree with a segment tree that supports range maximum queries and counts for non-strictly increasing sequences
C. No change needed; the original algorithm already counts sequences with reuse
D. Use the same algorithm but double the counts at each update to account for reuse
Solution
Step 1: Understand reuse and non-strictly increasing requirement
Allowing reuse means subsequences can have equal elements and elements can be chosen multiple times, breaking strict increasing assumption.
Step 2: Why Fenwick tree needs modification
Fenwick tree queries rely on strictly increasing order and unique indices; to handle reuse and non-strict sequences, segment tree with range max and count queries is needed to correctly aggregate counts over ranges including equal elements.
Step 3: Evaluate options
Simply changing comparison or doubling counts is incorrect; original algorithm assumes strict increasing and no reuse.
Final Answer:
Option B -> Option B
Quick Check:
Segment tree supports flexible queries needed for reuse and non-strict sequences [OK]
Hint: Reuse breaks strict increasing; segment tree needed [OK]
Common Mistakes:
Assuming original Fenwick tree works
Trying to double counts naively
Ignoring non-strict condition
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.