101 Logo
onenoughtone

Solution Approach

Solution Overview

There are 2 main approaches to solve this problem:

  1. DFS with Memoization - Complex approach
  2. Topological Sort (Kahn's Algorithm) - Complex approach

Approach 1: DFS with Memoization

Let's approach this problem by thinking of the matrix as a directed graph, where each cell is a node, and there's an edge from cell A to cell B if B is adjacent to A and has a higher value.

Thinking Process: Since we can only move to cells with higher values, there are no cycles in the graph. This means we have a directed acyclic graph (DAG). To find the longest path in a DAG, we can use depth-first search (DFS) starting from each node.

For each cell, we explore all four adjacent cells with higher values and recursively find the longest path starting from those cells. We add 1 to the result to account for the current cell.

However, a naive DFS approach would lead to redundant calculations, as we might visit the same cell multiple times. To optimize this, we can use memoization to store the result for each cell once we've computed it.

Intuition: By using memoization, we ensure that we compute the longest path starting from each cell exactly once, which significantly improves the efficiency of our algorithm.

Algorithm:

  1. Create a memo table to store the longest path starting from each cell.
  2. Define a DFS function that takes the current cell's coordinates (i, j) as input:
  3. a. If the result for (i, j) is already in the memo table, return it.
  4. b. Initialize the longest path from (i, j) as 1 (counting the current cell).
  5. c. For each of the four adjacent cells (up, down, left, right):
  6. i. If the adjacent cell is within bounds and has a higher value than the current cell:
  7. - Recursively find the longest path starting from the adjacent cell.
  8. - Update the longest path from (i, j) if a longer path is found.
  9. d. Store the result in the memo table and return it.
  10. Call the DFS function for each cell in the matrix and find the maximum result.

Time Complexity:

O(m * n)

We visit each cell exactly once and perform constant work for each cell. The memoization ensures that we don't recompute the result for any cell.

Space Complexity:

O(m * n)

We use a memo table of size m * n to store the longest path starting from each cell. The recursion stack also uses O(m * n) space in the worst case.

Approach 2: Topological Sort (Kahn's Algorithm)

Another approach is to use topological sorting, which is a linear ordering of vertices in a DAG such that for every directed edge (u, v), vertex u comes before vertex v in the ordering.

Thinking Process: We can model the problem as finding the longest path in a DAG. In a topological sort, we process nodes in an order such that we process a node only after processing all nodes that have edges pointing to it.

For this problem, we can reverse the edges in our graph (i.e., create an edge from a cell with a higher value to a cell with a lower value). Then, we can use Kahn's algorithm for topological sorting:

  • Calculate the in-degree of each node (cell).
  • Start with nodes that have an in-degree of 0 (cells that have no adjacent cells with higher values).
  • Process these nodes and decrement the in-degree of their adjacent nodes.
  • Continue this process until all nodes are processed.

Intuition: By processing nodes in topological order, we ensure that when we process a node, we have already processed all nodes that can lead to it. This allows us to compute the longest path to each node efficiently.

Algorithm:

  1. Create a graph where each cell is a node, and there's an edge from a cell with a higher value to an adjacent cell with a lower value.
  2. Calculate the in-degree of each node (the number of adjacent cells with higher values).
  3. Initialize a queue with nodes that have an in-degree of 0 (cells that have no adjacent cells with higher values).
  4. Initialize a distances array where distances[i][j] represents the longest path ending at cell (i, j).
  5. While the queue is not empty:
  6. a. Dequeue a node (i, j).
  7. b. For each of the four adjacent cells (up, down, left, right):
  8. i. If the adjacent cell is within bounds and has a lower value than the current cell:
  9. - Update the longest path to the adjacent cell if a longer path is found.
  10. - Decrement the in-degree of the adjacent cell.
  11. - If the in-degree becomes 0, enqueue the adjacent cell.
  12. Return the maximum value in the distances array.

Time Complexity:

O(m * n)

We process each cell exactly once and perform constant work for each cell.

Space Complexity:

O(m * n)

We use additional space for the graph, in-degree array, queue, and distances array, all of which are of size m * n.

Pseudocode

solution.pseudo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function longestIncreasingPath(matrix):
if matrix is empty:
return 0
m = number of rows in matrix
n = number of columns in matrix
memo = 2D array of size m x n, initialized with 0
function dfs(i, j):
if memo[i][j] != 0:
return memo[i][j]
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # up, down, left, right
longest = 1 # count the current cell
for (di, dj) in directions:
ni = i + di
nj = j + dj
if 0 <= ni < m and 0 <= nj < n and matrix[ni][nj] > matrix[i][j]:
longest = max(longest, 1 + dfs(ni, nj))
memo[i][j] = longest
return longest
result = 0
for i from 0 to m-1:
for j from 0 to n-1:
result = max(result, dfs(i, j))
return result
ProblemSolutionCode
101 Logo
onenoughtone