Given a triangular array triangle[], find the minimum path sum from top to bottom. We start from the first cell in the first row, and at each step, we can move to one of the two adjacent cells in the next row — that is, if we are at index i in the current row, we can move to either index i or i + 1 in the next row.
The triangular array contains 1 cell in 1st row, 2 cells in 2nd row and so on.
Examples :
Input: triangle[][] = [[2],
[3, 7],
[8, 5, 6],
[6, 1, 9, 3]]
Output: 11
Explanation : The path is 2 -> 3 -> 5 -> 1, which results in a minimum sum of 2 + 3 + 5 + 1 = 11.Input: triangle[][] = [[3],
[6, 9],
[8, 7, 1],
[9, 6, 8, 2]]
Output: 15
Explanation: The path is 3 -> 9 -> 1 -> 2, which results in a minimum sum of 3 + 9 + 1 + 2 = 15.
Table of Content
[Naive Approach] - Using Recursion – O(2 ^ (n*n)) Time and O(n) Space
At any position in the triangle, we can move only to one of the two adjacent positions in the next row.
So, to find the minimum path sum from a current cell (i, j), we need to consider both choices:
- Move to (i+1, j)
- Move to (i+1, j+1)
In order to find the answer for a cell in a row, we take the minimum among these two choices.
//Driver Code Starts
#include <iostream>
#include <vector>
using namespace std;
//Driver Code Ends
int minSumPathRec(vector<vector<int>> &triangle, int i, int j) {
// Base case: no value below the last row,
// return 0
if (i == triangle.size())
return 0;
// find the min path sum from
// current cell till the last row
return triangle[i][j]
+ min(
minSumPathRec(triangle, i + 1, j),
minSumPathRec(triangle, i + 1, j + 1));
}
int minSumPath(vector<vector<int>> &triangle) {
return minSumPathRec(triangle, 0, 0);
}
//Driver Code Starts
int main() {
vector<vector<int>> triangle = {
{2},
{3, 9},
{1, 6, 7}
};
cout << minSumPath(triangle);
}
//Driver Code Ends
//Driver Code Starts
import java.util.ArrayList;
import java.util.List;
class GFG {
//Driver Code Ends
static int minSumPathRec(ArrayList<ArrayList<Integer>> triangle,
int i, int j) {
// Base case: no value below the last row,
// return 0
if (i == triangle.size())
return 0;
// find the min path sum from
// current cell till the last row
return triangle.get(i).get(j)
+ Math.min(
minSumPathRec(triangle, i+1, j),
minSumPathRec(triangle, i+1, j+1));
}
static int minSumPath(ArrayList<ArrayList<Integer> > triangle) {
return minSumPathRec(triangle, 0, 0);
}
//Driver Code Starts
public static void main(String[] args) {
ArrayList<ArrayList<Integer> > triangle
= new ArrayList<>();
triangle.add(new ArrayList<>(List.of(2)));
triangle.add(new ArrayList<>(List.of(3, 9)));
triangle.add(new ArrayList<>(List.of(1, 6, 7)));
System.out.println(minSumPath(triangle));
}
}
//Driver Code Ends
def minSumPathRec(triangle, i, j):
# Base case: no value below the last row,
# return 0
if i == len(triangle):
return 0
# find the min path sum from
# current cell till the last row
return triangle[i][j] + min(minSumPathRec(triangle, i+1, j),
minSumPathRec(triangle, i+1, j+1))
def minSumPath(triangle):
return minSumPathRec(triangle, 0, 0)
#Driver Code Starts
if __name__ == "__main__":
triangle = [
[2],
[3, 9],
[1, 6, 7]
]
print(minSumPath(triangle))
#Driver Code Ends
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
static int minSumPathRec(List<List<int>> triangle,
int i, int j) {
// Base case: no value below the last row,
// return 0
if (i == triangle.Count)
return 0;
// find the min path sum from
// current cell till the last row
return triangle[i][j]
+ Math.Min(
minSumPathRec(triangle, i+1, j),
minSumPathRec(triangle, i+1, j+1));
}
static int minSumPath(List<List<int> > triangle) {
return minSumPathRec(triangle, 0, 0);
}
//Driver Code Starts
static void Main() {
List<List<int> > triangle = new List<List<int>>{
new List<int>{2},
new List<int>{3, 9},
new List<int>{1, 6, 7}
};
Console.WriteLine(minSumPath(triangle));
}
}
//Driver Code Ends
function minSumPathRec(triangle, i, j) {
// Base case: no value below the last row,
// return 0
if (i === triangle.length)
return 0;
// find the min path sum from
// current cell till the last row
return triangle[i][j]
+ Math.min(
minSumPathRec(triangle, i+1, j),
minSumPathRec(triangle, i+1, j+1));
}
function minSumPath(triangle) {
return minSumPathRec(triangle, 0, 0);
}
//Driver Code Starts
// Driver code
const triangle = [[2], [3, 9], [1, 6, 7]];
console.log(minSumPath(triangle));
//Driver Code Ends
Output
6
[Better Approach - 1] - Using Memoization – O(n*n) Time and O(n*n) Space
We observe that some of the subproblems overlap.
Like, in order to compute the answer for cell (i, j), we need answers for cells (i+1, j) and (i+1, j+1), and in order to compute the answer for cell (i, j+1), we need answers for cells (i+1, j+1) and (i+1, j+2). Here, the subproblems (i+1, j+1) is computed twice. Therefore, in order to avoid recomputing the same subproblems multiple times, we can store their results in a DP table and reuse them whenever needed.
//Driver Code Starts
#include <iostream>
#include <vector>
using namespace std;
//Driver Code Ends
int minSumPathRec(vector<vector<int>> &triangle, int i,
int j, vector<vector<int>> &dp) {
// Base Case
if (i == triangle.size())
return 0;
// if the result for this subproblem is
// already computed then return it
if (dp[i][j] != -1)
return dp[i][j];
// Recurisve Case
return dp[i][j] = triangle[i][j] +
min(minSumPathRec(triangle, i+1, j, dp),
minSumPathRec(triangle, i+1, j+1, dp));
}
int minSumPath(vector<vector<int>> &triangle) {
int n = triangle.size();
// dp array to store the result
vector<vector<int>> dp(n, vector<int>(n, -1));
return minSumPathRec(triangle, 0, 0, dp);
}
//Driver Code Starts
int main() {
vector<vector<int>> triangle{{2}, {3, 9}, {1, 6, 7}};
cout << minSumPath(triangle);
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.ArrayList;
import java.util.List;
class GFG {
//Driver Code Ends
static int minSumPathRec(ArrayList<ArrayList<Integer>> triangle,
int i, int j, int[][] dp) {
// Base Case
if (i == triangle.size())
return 0;
// If the result for this subproblem is
// already computed then return it
if (dp[i][j] != -1)
return dp[i][j];
// Recursive Case
return dp[i][j] = triangle.get(i).get(j)
+ Math.min(minSumPathRec(triangle, i+1, j, dp),
minSumPathRec(triangle, i+1, j+1, dp));
}
static int minSumPath(ArrayList<ArrayList<Integer>> triangle) {
int n = triangle.size();
// dp array to store the result
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
dp[i][j] = -1;
return minSumPathRec(triangle, 0, 0, dp);
}
//Driver Code Starts
public static void main(String[] args) {
ArrayList<ArrayList<Integer> > triangle
= new ArrayList<>();
triangle.add(new ArrayList<>(List.of(2)));
triangle.add(new ArrayList<>(List.of(3, 9)));
triangle.add(new ArrayList<>(List.of(1, 6, 7)));
System.out.println(minSumPath(triangle));
}
}
//Driver Code Ends
def minSumPathRec(triangle, i, j, dp):
# Base Case
if i == len(triangle):
return 0
# if the result for this subproblem is
# already computed then return it
if dp[i][j] != -1:
return dp[i][j]
# Recursive Case
dp[i][j] = triangle[i][j] + min(minSumPathRec(triangle, i+1, j, dp),
minSumPathRec(triangle, i+1, j+1, dp))
return dp[i][j]
def minSumPath(triangle):
n = len(triangle)
# dp array to store the result
dp = [[-1] * n for i in range(n)]
return minSumPathRec(triangle, 0, 0, dp)
#Driver Code Starts
triangle = [
[2],
[3, 9],
[1, 6, 7]
]
print(minSumPath(triangle))
#Driver Code Ends
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
static int minSumPathRec(List<List<int>> triangle,
int i, int j, int[][] dp) {
// Base Case
if (i == triangle.Count)
return 0;
// If the result for this subproblem is
// already computed then return it
if (dp[i][j] != -1)
return dp[i][j];
// Recursive Case
dp[i][j] = triangle[i][j] +
Math.Min(minSumPathRec(triangle, i+1, j, dp),
minSumPathRec(triangle, i+1, j+1, dp));
return dp[i][j];
}
static int minSumPath(List<List<int> > triangle) {
int n = triangle.Count;
// dp array to store the result
int[][] dp = new int[n][];
for (int i = 0; i < n; i++) {
dp[i] = new int[n];
Array.Fill(dp[i], -1);
}
return minSumPathRec(triangle, 0, 0, dp);
}
//Driver Code Starts
static void Main() {
List<List<int> > triangle = new List<List<int>>{
new List<int>{2}, new List<int>{3, 9},
new List<int>{1, 6, 7}
};
Console.WriteLine(minSumPath(triangle));
}
}
//Driver Code Ends
function minSumPathRec(triangle, i, j, dp) {
// Base Case
if (i === triangle.length)
return 0;
// if the result for this subproblem is
// already computed then return it
if (dp[i][j] !== -1)
return dp[i][j];
// Recursive Case
dp[i][j] = triangle[i][j] +
Math.min(minSumPathRec(triangle, i+1, j, dp),
minSumPathRec(triangle, i+1, j+1, dp));
return dp[i][j];
}
function minSumPath(triangle) {
const n = triangle.length;
// dp array to store the result
const dp
= Array.from({length : n}, () => Array(n).fill(-1));
return minSumPathRec(triangle, 0, 0, dp);
}
//Driver Code Starts
const triangle = [[2], [3, 9], [1, 6, 7]];
console.log(minSumPath(triangle));
//Driver Code Ends
Output
6
[Better Approach - 2] - Using Tabulation – O(n*n) Time and O(n*n) Space
In this approach, we iteratively calculate the answers starting from the smallest subproblems — the last row of the triangle. The answer for any row depends on the answers of the rows below it. Using the precomputed results from the next row, we can efficiently compute the minimum path sums for the current row and store them for further use, continuing this process upward until we reach the top.
//Driver Code Starts
#include <iostream>
#include <vector>
using namespace std;
//Driver Code Ends
int minSumPath(vector<vector<int>> &triangle) {
int n = triangle.size();
// DP array to store the result
vector<vector<int>> dp(n, vector<int>(n));
// the last row will be same as triangle
for (int i = 0; i < n; i++)
dp[n-1][i] = triangle[n - 1][i];
// Calculation of the remaining rows,
// in bottom up manner
for (int i = n - 2; i >= 0; i--)
for (int j = 0; j < triangle[i].size(); j++)
dp[i][j] = triangle[i][j] +
min(dp[i+1][j], dp[i+1][j+1]);
return dp[0][0];
}
//Driver Code Starts
int main() {
vector<vector<int> > triangle{{2},
{3, 9},
{1, 6, 7}};
cout << minSumPath(triangle);
return 0;
}
//Driver Code Ends
//Driver Code Starts
import java.util.ArrayList;
import java.util.List;
class GFG {
//Driver Code Ends
static int minSumPath(ArrayList<ArrayList<Integer>> triangle) {
int n = triangle.size();
// DP array to store the result
int[][] dp = new int[n][n];
// the last row will be same as triangle
for( int j = 0; j < n; j++ )
dp[n-1][j] = triangle.get(n-1).get(j);
// Calculation of the remaining rows,
// in bottom up manner
for( int i = n-2 ; i >= 0 ; i-- ) {
for( int j = 0; j <= i; j++ ) {
dp[i][j] = triangle.get(i).get(j) +
Math.min(dp[i+1][j], dp[i+1][j+1]);
}
}
return dp[0][0];
}
//Driver Code Starts
public static void main(String[] args) {
ArrayList<ArrayList<Integer>> triangle
= new ArrayList<>();
triangle.add(new ArrayList<>(List.of(2)));
triangle.add(new ArrayList<>(List.of(3, 9)));
triangle.add(new ArrayList<>(List.of(1, 6, 7)));
System.out.println(minSumPath(triangle));
}
}
//Driver Code Ends
def minSumPath(triangle):
n = len(triangle)
# DP array to store the result
dp = [[0] * n for _ in range(n)]
# The last row will be the same as triangle
for i in range(n):
dp[n - 1][i] = triangle[n - 1][i]
# Calculation of the remaining rows,
# in bottom up manner
for i in range(n - 2, -1, -1):
for j in range(len(triangle[i])):
dp[i][j] = triangle[i][j] + \
min(dp[i+1][j], dp[i+1][j+1])
return dp[0][0]
#Driver Code Starts
if __name__ == "__main__":
triangle = [[2],
[3, 9],
[1, 6, 7]]
print(minSumPath(triangle))
#Driver Code Ends
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
static int minSumPath(List<List<int>> triangle) {
int n = triangle.Count;
// DP array to store the result
int[][] dp = new int[n][];
for (int i = 0; i < n; i++) {
dp[i] = new int[n];
}
// The last row will be the same as triangle
for (int i = 0; i < n; i++)
dp[n - 1][i] = triangle[n - 1][i];
// Calculation of the remaining rows,
// in bottom up manner
for (int i = n - 2; i >= 0; i--)
for (int j = 0; j < triangle[i].Count; j++)
dp[i][j] = triangle[i][j]
+ Math.Min(dp[i+1][j], dp[i+1][j+1]);
return dp[0][0];
}
//Driver Code Starts
static void Main() {
List<List<int> > triangle = new List<List<int>>{
new List<int>{2},
new List<int>{3, 9},
new List<int>{1, 6, 7}
};
Console.WriteLine(minSumPath(triangle));
}
}
//Driver Code Ends
function minSumPath(triangle) {
const n = triangle.length;
// DP array to store the result
const dp = Array.from(Array(n), () => new Array(n));
// The last row will be the same as triangle
for (let i = 0; i < n; i++)
dp[n - 1][i] = triangle[n - 1][i];
// Calculation of the remaining rows,
// in bottom up manner
for (let i = n - 2; i >= 0; i--)
for (let j = 0; j < triangle[i].length; j++)
dp[i][j] = triangle[i][j]
+ Math.min(dp[i+1][j], dp[i+1][j+1]);
return dp[0][0];
}
//Driver Code Starts
//Driver code
const triangle = [[2], [3, 9], [1, 6, 7]];
console.log(minSumPath(triangle));
//Driver Code Ends
Output
6
[Expected Approach] - Using Space Optimized DP – O(n*n) Time and O(n) Space
In the above approach, we observe that in order to calculate the result for any cell in a row, we only need the answers of the next row in the triangle, specifically the two adjacent values directly below. Thus, while solving for a row, we use the answers of next row and store the answers for current one.
//Driver Code Starts
#include <vector>
#include <iostream>
using namespace std;
//Driver Code Ends
int minSumPath(vector<vector<int>> &triangle) {
int n = triangle.size();
// 1-D dp to store the result
vector<int> dp(n);
// Base Case: Initially dp will be the
// same as last row of matrix
for (int i = 0; i < n; i++)
dp[i] = triangle[n - 1][i];
// Calculation of the remaining rows,
// in bottom up manner
for (int i = triangle.size() - 2; i >= 0; i--) {
vector<int> new_dp(i + 1);
for (int j = 0; j < triangle[i].size(); j++) {
// calculating answers for current row
new_dp[j] = triangle[i][j] + min(dp[j], dp[j + 1]);
}
dp = new_dp;
}
return dp[0];
}
//Driver Code Starts
int main() {
vector<vector<int>> triangle = {
{2},
{3, 9},
{1, 6, 7}
};
cout << minSumPath(triangle);
}
//Driver Code Ends
//Driver Code Starts
import java.util.ArrayList;
import java.util.List;
class GFG {
//Driver Code Ends
static int minSumPath(ArrayList<ArrayList<Integer>> triangle) {
int n = triangle.size();
// 1-D dp to store the result
int[] dp = new int[n];
// Base Case: Initially dp will be the
// same as last row of matrix
for (int i = 0; i < n; i++)
dp[i] = triangle.get(n - 1).get(i);
// Calculation of the remaining rows,
// in bottom up manner
for (int i = triangle.size() - 2; i >= 0; i--) {
int[] new_dp = new int[i+1];
for (int j = 0; j < triangle.get(i).size(); j++) {
// calculating answers for current row
new_dp[j] = triangle.get(i).get(j) + Math.min(dp[j], dp[j + 1]);
}
dp = new_dp;
}
return dp[0];
}
//Driver Code Starts
public static void main(String[] args) {
ArrayList<ArrayList<Integer> > triangle
= new ArrayList<>();
triangle.add(new ArrayList<>(List.of(2)));
triangle.add(new ArrayList<>(List.of(3, 9)));
triangle.add(new ArrayList<>(List.of(1, 6, 7)));
System.out.println(minSumPath(triangle));
}
}
//Driver Code Ends
def minSumPath(triangle):
n = len(triangle)
# 1-D dp to store the result
dp = [0] * n
# Base Case: Initially dp will be the
# same as last row of matrix
for i in range(n):
dp[i] = triangle[n - 1][i]
# Calculation of the remaining rows,
# in bottom up manner
for i in range(len(triangle) - 2, -1, -1):
new_dp = [0] * (i + 1)
for j in range(len(triangle[i])):
# calculating answers for current row
new_dp[j] = triangle[i][j] + min(dp[j], dp[j + 1])
dp = new_dp
return dp[0]
#Driver Code Starts
if __name__ == "__main__":
triangle = [
[2],
[3, 9],
[1, 6, 7]
]
print(minSumPath(triangle))
#Driver Code Ends
//Driver Code Starts
using System;
using System.Collections.Generic;
class GFG {
//Driver Code Ends
static int minSumPath(List<List<int>> triangle) {
int n = triangle.Count;
// 1-D dp to store the result
int[] dp = new int[n];
// Base Case: Initially dp will be the
// same as last row of matrix
for (int i = 0; i < n; i++)
dp[i] = triangle[n - 1][i];
// Calculation of the remaining rows,
// in bottom up manner
for (int i = triangle.Count - 2; i >= 0; i--) {
int[] new_dp = new int[i + 1];
for (int j = 0; j < triangle[i].Count; j++) {
// calculating answers for current row
new_dp[j] = triangle[i][j] + Math.Min(dp[j], dp[j + 1]);
}
dp = new_dp;
}
return dp[0];
}
//Driver Code Starts
static void Main() {
var triangle = new List<List<int>> {
new List<int> {2},
new List<int> {3, 9},
new List<int> {1, 6, 7}
};
Console.WriteLine(minSumPath(triangle));
}
}
//Driver Code Ends
function minSumPath(triangle) {
const n = triangle.length;
// 1-D dp to store the result
let dp = new Array(n);
// Base Case: Initially dp will be the
// same as last row of matrix
for (let i = 0; i < n; i++)
dp[i] = triangle[n - 1][i];
// Calculation of the remaining rows,
// in bottom up manner
for (let i = triangle.length - 2; i >= 0; i--) {
let new_dp = new Array(i + 1);
for (let j = 0; j < triangle[i].length; j++) {
// calculating answers for current row
new_dp[j] = triangle[i][j] + Math.min(dp[j], dp[j + 1]);
}
dp = new_dp;
}
return dp[0];
}
//Driver Code Starts
// Driver code
const triangle = [
[2],
[3, 9],
[1, 6, 7]
];
console.log(minSumPath(triangle));
//Driver Code Ends
Output
6