Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1301. Number of Paths with Max Score - Leetcode Solution

Problem Description

The "Number of Paths with Max Score" problem gives you a square board represented as a list of strings, where each cell is one of the following:

  • A digit ('1'-'9'), representing the score you can collect by stepping on it
  • 'S', the starting cell (bottom-right corner)
  • 'E', the ending cell (top-left corner)
  • 'X', an obstacle you cannot pass through

You can only move up, left, or up-left diagonally at each step. Your goal is to find:

  1. The maximum total score you can collect on any path from 'S' to 'E'
  2. The number of distinct paths that achieve this maximum score

Return an array [maxScore, numPaths], where maxScore is the largest possible sum, and numPaths is the number of such paths modulo 109+7. If there is no valid path, return [0, 0].

You must not step on obstacles, and you cannot revisit cells.

Constraints:

  • The board is a square matrix with 1 <= n <= 100
  • All moves are within bounds and only up, left, or up-left

Thought Process

To solve this problem, let's start by thinking about brute force: we could try every possible path from 'S' to 'E', summing scores and counting the max. But with up to 100x100 cells, the number of paths grows exponentially—making brute-force infeasible.

Instead, we notice that this problem fits the pattern of dynamic programming. At each cell, the best score and number of ways to get there depends only on the best results from the three possible previous cells (down, right, or down-right from the perspective of the board, since we are moving up, left, or up-left). This means we can build up a solution by filling in a DP table, reusing results for efficiency.

The main challenge is to keep track of both the maximum score and the number of ways to achieve it at each cell. At obstacles, we store zeroes. For other cells, we look at the three possible moves and combine their results, always taking care to handle ties in max score.

Solution Approach

We'll use a 2D dynamic programming table, where each cell dp[i][j] stores a pair: (maxScore, numWays) to reach that cell from the start.

  • Initialize the DP table with (-1, 0) everywhere, except for the starting cell 'S' (bottom-right), which is (0, 1) because you start with score 0 and 1 way.
  • Process the board from bottom-right to top-left (since we can only move up, left, or up-left).
  • For each cell, if it is an obstacle ('X'), skip it.
  • For each cell, check the three possible previous cells (down, right, down-right), and for each:
    • If the previous cell has a valid score, calculate the new score by adding the current cell's value (unless 'S' or 'E', which are treated as 0).
    • If this new score is greater than the current max, set the max and reset the path count.
    • If this new score equals the current max, add the number of ways.
  • After filling the table, the answer is in dp[0][0] (top-left, 'E'). If no path exists, return [0, 0].

We use modulo 10^9 + 7 for the path count to avoid overflow.

Example Walkthrough

Let's consider this board:

    ["E23",
     "2X2",
     "12S"]
    
  • Start at 'S' (2,2). Only move up, left, or up-left.
  • From 'S', you can move to (1,2) or (2,1). Both are '2'.
  • Continue moving, avoiding 'X' at (1,1).
  • Possible paths:
    • 2,2 → 1,2 → 0,2 → 0,1 → 0,0 ('E')
    • 2,2 → 2,1 → 1,1 (blocked) or 2,0 → ...
  • By tracing all valid paths, we find the path with max score: 2 (from 'S') + 2 (from (1,2)) + 3 (from (0,2)) + 2 (from (0,1)) = 7. There is only one such path.

So the output is [7, 1].

Time and Space Complexity

  • Brute-force: Exponential time and space, as every path is explored individually. Not feasible for large boards.
  • Dynamic Programming:
    • Time Complexity: O(n2), since we process each cell once and look at three neighbors.
    • Space Complexity: O(n2), for the DP table storing (score, count) pairs.

Summary

The "Number of Paths with Max Score" problem is a classic dynamic programming challenge. By breaking the problem into subproblems—tracking the max score and number of ways at each cell—we avoid redundant calculations and efficiently find the result. The solution elegantly combines path counting and score maximization by leveraging a 2D DP table and careful state updates.

Code Implementation

class Solution:
    def pathsWithMaxScore(self, board):
        n = len(board)
        MOD = 10**9 + 7
        dp = [[(-1, 0) for _ in range(n)] for _ in range(n)]
        dp[n-1][n-1] = (0, 1)
        for i in range(n-1, -1, -1):
            for j in range(n-1, -1, -1):
                if board[i][j] == 'X' or (i == n-1 and j == n-1):
                    continue
                maxScore = -1
                ways = 0
                for dx, dy in [(1,0), (0,1), (1,1)]:
                    ni, nj = i+dx, j+dy
                    if 0 <= ni < n and 0 <= nj < n:
                        prevScore, prevWays = dp[ni][nj]
                        if prevWays == 0:
                            continue
                        val = 0 if board[i][j] in 'SE' else int(board[i][j])
                        currScore = prevScore + val
                        if currScore > maxScore:
                            maxScore = currScore
                            ways = prevWays
                        elif currScore == maxScore:
                            ways = (ways + prevWays) % MOD
                if maxScore != -1:
                    dp[i][j] = (maxScore, ways % MOD)
        result = dp[0][0]
        return [result[0], result[1]] if result[1] != 0 else [0,0]
      
class Solution {
public:
    vector<int> pathsWithMaxScore(vector<string>& board) {
        int n = board.size(), MOD = 1e9+7;
        vector<vector<pair<int,int>>> dp(n, vector<pair<int,int>>(n, {-1, 0}));
        dp[n-1][n-1] = {0, 1};
        for (int i = n-1; i >= 0; --i) {
            for (int j = n-1; j >= 0; --j) {
                if (board[i][j] == 'X' || (i == n-1 && j == n-1)) continue;
                int maxScore = -1, ways = 0;
                for (auto d : vector<pair<int,int>>{{1,0},{0,1},{1,1}}) {
                    int ni = i + d.first, nj = j + d.second;
                    if (ni < n && nj < n && dp[ni][nj].second > 0) {
                        int val = (board[i][j] == 'S' || board[i][j] == 'E') ? 0 : board[i][j] - '0';
                        int currScore = dp[ni][nj].first + val;
                        if (currScore > maxScore) {
                            maxScore = currScore;
                            ways = dp[ni][nj].second;
                        } else if (currScore == maxScore) {
                            ways = (ways + dp[ni][nj].second) % MOD;
                        }
                    }
                }
                if (maxScore != -1)
                    dp[i][j] = {maxScore, ways};
            }
        }
        auto res = dp[0][0];
        if (res.second == 0) return {0,0};
        return {res.first, res.second};
    }
};
      
class Solution {
    public int[] pathsWithMaxScore(List<String> board) {
        int n = board.size(), MOD = 1000000007;
        int[][][] dp = new int[n][n][2];
        for (int[][] arr : dp) for (int[] a : arr) java.util.Arrays.fill(a, -1);
        dp[n-1][n-1][0] = 0;
        dp[n-1][n-1][1] = 1;
        for (int i = n-1; i >= 0; --i) {
            for (int j = n-1; j >= 0; --j) {
                if (board.get(i).charAt(j) == 'X' || (i == n-1 && j == n-1)) continue;
                int maxScore = -1, ways = 0;
                int[][] dirs = {{1,0},{0,1},{1,1}};
                for (int[] d : dirs) {
                    int ni = i + d[0], nj = j + d[1];
                    if (ni < n && nj < n && dp[ni][nj][1] > 0) {
                        int val = (board.get(i).charAt(j) == 'S' || board.get(i).charAt(j) == 'E') ? 0 : board.get(i).charAt(j) - '0';
                        int currScore = dp[ni][nj][0] + val;
                        if (currScore > maxScore) {
                            maxScore = currScore;
                            ways = dp[ni][nj][1];
                        } else if (currScore == maxScore) {
                            ways = (ways + dp[ni][nj][1]) % MOD;
                        }
                    }
                }
                if (maxScore != -1) {
                    dp[i][j][0] = maxScore;
                    dp[i][j][1] = ways;
                }
            }
        }
        if (dp[0][0][1] == 0) return new int[]{0,0};
        return new int[]{dp[0][0][0], dp[0][0][1]};
    }
}
      
var pathsWithMaxScore = function(board) {
    const n = board.length, MOD = 1e9+7;
    const dp = Array.from({length: n}, () => Array.from({length: n}, () => [-1, 0]));
    dp[n-1][n-1] = [0, 1];
    for (let i = n-1; i >= 0; --i) {
        for (let j = n-1; j >= 0; --j) {
            if (board[i][j] === 'X' || (i === n-1 && j === n-1)) continue;
            let maxScore = -1, ways = 0;
            for (const [dx, dy] of [[1,0],[0,1],[1,1]]) {
                const ni = i + dx, nj = j + dy;
                if (ni < n && nj < n && dp[ni][nj][1] > 0) {
                    const val = (board[i][j] === 'S' || board[i][j] === 'E') ? 0 : Number(board[i][j]);
                    const currScore = dp[ni][nj][0] + val;
                    if (currScore > maxScore) {
                        maxScore = currScore;
                        ways = dp[ni][nj][1];
                    } else if (currScore === maxScore) {
                        ways = (ways + dp[ni][nj][1]) % MOD;
                    }
                }
            }
            if (maxScore !== -1)
                dp[i][j] = [maxScore, ways];
        }
    }
    const [maxScore, ways] = dp[0][0];
    return ways === 0 ? [0,0] : [maxScore, ways];
};