1289. Minimum Falling Path Sum II
Problem Description
Given an n x n
integer matrix named grid
, the objective is to calculate the minimum sum of what is termed a "falling path with non-zero shifts." A falling path with non-zero shifts is a selection of one element from each row of the matrix such that for any two elements chosen from consecutive rows, they are not situated in the same column. Essentially, we need to find a path from the top to the bottom of the matrix, selecting one element from each row, without reusing columns in consecutive rows, and adding those elements to achieve the smallest possible sum.
Intuition
To find the minimum falling path sum, we can use a dynamic programming approach that builds upon the answer from the previous row to compute the answer for the current row. Specifically, for each row, we keep track of the smallest value (f
) and the second smallest (g
) from the previous row. This is because we cannot pick elements from the same column in consecutive rows, so we need to have a backup option in case the smallest value from the previous row is in the same column.
The solution progressively calculates the sum of each row, taking into account the smallest sums of the previous row while avoiding the same column positions. For each row, we iterate through its columns and try to find the smallest sum (ff
) by either choosing the smallest sum for different columns or the second smallest sum (gg
) when the same column as the previous smallest is encountered. This way, we prevent two consecutive rows from having elements in the same column. By keeping an updated minimum (f
) and second minimum (g
) and their column positions (fp
), we can continue to build up the solution row by row, until we reach the bottom of the matrix. The minimum sum of the last calculated row will be the final answer, which provides the minimum sum of a falling path with non-zero shifts as required.
Learn more about Dynamic Programming patterns.
Solution Approach
The solution approach makes use of dynamic programming as its core strategy. The idea is to iterate over each row and compute the minimum and the second minimum values required to reach each position without considering the previous row's column. By doing so, we ensure compliance with the problem's condition that no two elements in adjacent rows are in the same column.
A crucial aspect of the solution is tracking the minimum (f
) and the second minimum (g
) costs to reach the current row while recording the column index (fp
) of the minimum cost from the previous row. This information is used to decide whether to use the minimum or second minimum cost when calculating the current row’s values.
Here's a walkthrough of the algorithm using the problem's terminology:
-
Initialize
f
andg
to 0 as the initial minimum and second minimum costs, respectively. Initializefp
to -1 to indicate no previous column. -
For each row
row
in the matrixgrid
:- Initialize
ff
andgg
toinf
as placeholders for the current row's minimum and second minimum costs. - Initialize
ffp
to -1, which will hold the column index of the current row's minimum cost. - For each value
v
inrow
, with its indexj
:- Calculate the sum
s
by addingv
tog
if the current columnj
is the same asfp
(to avoid picking two elements from the same column in adjacent rows), or tof
otherwise. - If the calculated sum
s
is less thanff
:- Assign the value of
ff
togg
, asff
will now hold the new minimum,s
. - Update
ff
with the new minimum,s
. - Update
ffp
with the current column indexj
, indicating the column of the new minimum.
- Assign the value of
- Else if
s
is less thangg
, updategg
with the new second minimum,s
.
- Calculate the sum
- Initialize
-
After the loop, assign
ff
tof
,gg
tog
, andffp
tofp
for the next iteration. This step effectively moves the minimum and second minimum costs, along with the column index of the minimum, to the next row. -
Once all rows have been processed,
f
holds the minimum sum of a falling path with non-zero shifts and is returned as the result.
Throughout this process, the algorithm avoids using the same column for consecutive rows by considering g
(the second smallest) whenever the current column is the same as the previous row's minimum. By keeping a running tally of the smallest sums that conform to the problem's rules, the algorithm ensures optimal substructure—one of the hallmarks of dynamic programming. In the end, the minimum sum of the final row provides the answer.
This solution has a time complexity of O(n^2)
where n
is the number of rows (or columns) in the matrix, making it efficient enough to handle larger matrices.
The code is a practical implementation of this approach and manifests the described dynamic programming strategy effectively.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's use a 3x3 matrix as a small example to illustrate the solution approach.
matrix grid: [ [2, 1, 3], [6, 5, 4], [7, 8, 9] ]
Following the dynamic programming approach to find the minimum falling path sum with non-zero shifts:
- Initialize
f
andg
to 0, andfp
to -1 (no previous column):
f = 0, g = 0, fp = -1
- For the first row, since there are no previous rows, the minimum (
f
) and second minimum (g
) sums are the smallest and second smallest values from the row itself. We then determinef
,g
, andfp
:
Row 0: [2, 1, 3] f = 1, g = 2, fp = 1 (index of minimum value in row 0)
- Move to the second row, update
ff
,gg
, andffp
as we find the sums for this row:
Row 1: [6, 5, 4], Previous `fp` = 1 For column 0: s = grid[1][0] + g = 6 + 2 = 8 (since column 0 is not `fp`) For column 1: Cannot choose since `fp` is 1 (same column as the previous minimum) For column 2: s = grid[1][2] + f = 4 + 1 = 5 (since column 2 is not `fp`) Update `f`, `g`, and `fp` for this row: ff = 5, gg = 8, ffp = 2
- After iterating through the second row, update
f
,g
, andfp
with the new values:
f = ff = 5, g = gg = 8, fp = ffp = 2
- Repeat the process for the third row:
Row 2: [7, 8, 9], Previous `fp` = 2 For column 0: s = grid[2][0] + g = 7 + 8 = 15 (since column 0 is not `fp`) For column 1: s = grid[2][1] + f = 8 + 5 = 13 (since column 1 is not `fp`) For column 2: Cannot choose since `fp` is 2 (same column as the previous minimum) Only two valid sums for this row: ff = 13, gg = 15, ffp = 1
- After the final row, we update
f
,g
, andfp
for the last time:
f = ff = 13, g = gg = 15, fp = ffp = 1
The minimum sum of a falling path with non-zero shifts is held in f
, which is 13 in this case.
This completes the example for the 3x3 matrix, the path that leads to the minimum sum would be from the elements grid[0][1], grid[1][2], and grid[2][1], resulting in the path 1 -> 4 -> 8, with a sum of 13.
Solution Implementation
1class Solution:
2 def minFallingPathSum(self, matrix: List[List[int]]) -> int:
3 # Initialize the smallest and second smallest falls from the previous row ("f" and "g").
4 # Initialize the position of the smallest fall in the previous row ("f_position").
5 smallest_fall = second_smallest_fall = float('inf')
6 smallest_fall_position = -1
7
8 # Iterate over each row in the matrix.
9 for row in matrix:
10 # Initialize the smallest and second smallest falls of the current row
11 # ("current_smallest" and "current_second_smallest"), and the position of
12 # the smallest fall in the current row ("current_smallest_position").
13 current_smallest = current_second_smallest = float('inf')
14 current_smallest_position = -1
15
16 # Iterate over each value in the current row with its index.
17 for j, value in enumerate(row):
18 # Determine whether to use the smallest or second smallest fall from
19 # the previous row to add to the current element.
20 total_path_sum = (second_smallest_fall if j == smallest_fall_position else smallest_fall) + value
21 # Update the smallest or second smallest fall if necessary.
22 if total_path_sum < current_smallest:
23 current_second_smallest = current_smallest
24 current_smallest = total_path_sum
25 current_smallest_position = j
26 elif total_path_sum < current_second_smallest:
27 current_second_smallest = total_path_sum
28
29 # Move to the next row: update the smallest and second smallest falls and the position
30 smallest_fall, second_smallest_fall, smallest_fall_position = (
31 current_smallest, current_second_smallest, current_smallest_position
32 )
33
34 # The matrix has been processed, "smallest_fall" contains the minimum falling path sum.
35 return smallest_fall
36
1class Solution {
2 public int minFallingPathSum(int[][] grid) {
3 int firstMin = 0; // The smallest sum of the falling path so far.
4 int secondMin = 0; // The second smallest sum of the falling path so far.
5 int firstMinPos = -1; // Position of the smallest sum in the previous row.
6 final int INF = Integer.MAX_VALUE; // Set a high value to represent the initial state that's effectively infinity.
7
8 // Iterating through each row in the grid.
9 for (int[] row : grid) {
10 int curFirstMin = INF; // The smallest sum in the current row.
11 int curSecondMin = INF; // The second smallest sum in the current row.
12 int curFirstMinPos = -1; // Position of the smallest sum in the current row.
13
14 // Iterating through each element in the current row.
15 for (int j = 0; j < row.length; ++j) {
16 // Calculate the sum by adding the current element to the smaller of the two sums from the previous row,
17 // but not the one directly above (to avoid falling path through same column, hence j != firstMinPos check).
18 int sum = (j != firstMinPos ? firstMin : secondMin) + row[j];
19
20 // If the calculated sum is smaller than the current smallest sum, update the first and second min values and positions.
21 if (sum < curFirstMin) {
22 curSecondMin = curFirstMin; // The smallest becomes the second smallest.
23 curFirstMin = sum; // The current sum becomes the new smallest.
24 curFirstMinPos = j; // Update the current smallest sum position.
25 } else if (sum < curSecondMin) { // If the current sum is between first and second min, update the second min only.
26 curSecondMin = sum;
27 }
28 }
29
30 // Prepare for next row.
31 firstMin = curFirstMin;
32 secondMin = curSecondMin;
33 firstMinPos = curFirstMinPos;
34 }
35
36 // After processing all rows, the smallest sum will represent the minimum sum of a falling path.
37 return firstMin;
38 }
39}
40
1#include <vector>
2#include <climits> // For INT_MAX usage
3
4class Solution {
5public:
6 int minFallingPathSum(vector<vector<int>>& grid) {
7 int n = grid.size(); // Get the size of the grid.
8
9 // Using descriptive names for variables to indicate their usage.
10 int minFirstPathSum = 0; // Stores the minimum sum of the first path.
11 int minSecondPathSum = 0; // Stores the minimum sum of the second best path.
12 int prevMinPathCol = -1; // Index of the column resulting in the minimum sum in the previous row.
13
14 // Infinity substitute for initialization (could use INT_MAX from <climits>).
15 const int kInfinity = INT_MAX;
16
17 // Iterate over each row in the input grid.
18 for (auto& row : grid) {
19 int newMinFirstPathSum = kInfinity; // The new minimum sum of the first path.
20 int newMinSecondPathSum = kInfinity; // The new minimum sum of the second best path.
21 int newMinPathCol = -1; // Column index for the new minimum sum.
22
23 // Iterate over each element in the current row.
24 for (int j = 0; j < n; ++j) {
25 int currentSum = (prevMinPathCol != j ? minFirstPathSum : minSecondPathSum) + row[j];
26
27 // If the current sum is less than the new minimum, update both the first and second best sums.
28 if (currentSum < newMinFirstPathSum) {
29 newMinSecondPathSum = newMinFirstPathSum; // Previous min becomes second best.
30 newMinFirstPathSum = currentSum; // Current sum becomes the new min.
31 newMinPathCol = j; // Update the column index for new min.
32 } else if (currentSum < newMinSecondPathSum) {
33 newMinSecondPathSum = currentSum; // Update the second best path sum if current is less than it.
34 }
35 }
36
37 // Update the path sums and the column index for the next row iteration.
38 minFirstPathSum = newMinFirstPathSum;
39 minSecondPathSum = newMinSecondPathSum;
40 prevMinPathCol = newMinPathCol;
41 }
42 return minFirstPathSum; // Return the minimum sum of a falling path.
43 }
44};
45
1// Import required modules (if needed in the context where this code will run).
2// In vanilla TypeScript, importing modules like this isn't necessary.
3// import { INT_MAX } from 'constants';
4
5// Declare a constant to represent infinity (as a substitute for INT_MAX).
6const kInfinity: number = Number.MAX_SAFE_INTEGER;
7
8// Global variables acting as state (simulating class member variables)
9let minFirstPathSum: number = 0; // Stores the minimum sum of the first path.
10let minSecondPathSum: number = 0; // Stores the minimum sum of the second best path.
11let prevMinPathCol: number = -1; // Index of the column resulting in the minimum sum in the previous row.
12
13function minFallingPathSum(grid: number[][]): number {
14 const n: number = grid.length; // Get the size of the grid.
15
16 // Iterate over each row in the input grid.
17 for (let row of grid) {
18 let newMinFirstPathSum: number = kInfinity; // The new minimum sum of the first path.
19 let newMinSecondPathSum: number = kInfinity; // The new minimum sum of the second best path.
20 let newMinPathCol: number = -1; // Column index for the new minimum sum.
21
22 // Iterate over each element in the current row.
23 for (let j = 0; j < n; ++j) {
24 let currentSum: number = (prevMinPathCol !== j ? minFirstPathSum : minSecondPathSum) + row[j];
25
26 // If the current sum is less than the new minimum, update both the first and second best sums.
27 if (currentSum < newMinFirstPathSum) {
28 newMinSecondPathSum = newMinFirstPathSum; // Previous min becomes second best.
29 newMinFirstPathSum = currentSum; // Current sum becomes the new min.
30 newMinPathCol = j; // Update the column index for new min.
31 } else if (currentSum < newMinSecondPathSum) {
32 newMinSecondPathSum = currentSum; // Update the second best path sum if current is less than it.
33 }
34 }
35
36 // Update the path sums and the column index for the next row iteration.
37 minFirstPathSum = newMinFirstPathSum;
38 minSecondPathSum = newMinSecondPathSum;
39 prevMinPathCol = newMinPathCol;
40 }
41
42 return minFirstPathSum; // Return the minimum sum of a falling path.
43}
44
Time and Space Complexity
Time Complexity
The time complexity of the code is O(n * m)
, where n
is the number of rows and m
is the number of columns in the input grid. This is because the code iterates through each row with an outer loop and each column with an inner loop, computing the minimum falling path sum in a single pass.
Space Complexity
The space complexity of the code is O(1)
. Although the algorithm iterates through the entire grid, it uses only a constant amount of additional space for the variables f
, g
, fp
, ff
, gg
, and ffp
, regardless of the size of the input grid.
Learn more about how to find time and space complexity quickly using problem constraints.
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!