Start learning free

LESSON 326 OF 347 · DYNAMIC PROGRAMMING

Unique Paths in C#: Grid Dynamic Programming with Obstacles

Practice O(n * m) Read the full explanation

A robot is located at the top-left corner of a n x m grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any moment of time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

Now consider obstacles are placed in some of the grid's cells. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

Note: n and m will be at most 100.

It is guaranteed that the answer can be represented as a 32-bit integer.

Example 1:

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
STEP 1 OF 13
    Ctrl + Enter to check

    C# · DYNAMIC PROGRAMMING

    Unique Paths in C#, explained

    Problem overview

    A robot stands in the top-left cell of a grid and wants to reach the bottom-right cell. It can only move down or right. Some cells hold obstacles. In C#, the grid is an int[,] passed to UniquePaths(int[,] grid), where 1 marks an obstacle and 0 an empty cell. The method returns the number of different paths from corner to corner.

    The example is a 3x3 grid with a single obstacle in the center. Two paths exist: right, right, down, down, and down, down, right, right. The answer fits in a 32-bit int, as the problem guarantees, and the grid has at most 100 rows and 100 columns.

    Approach: count the ways into each cell

    Listing every path is hopeless: a 100x100 grid has an astronomical number of them. Count them instead. Let dp[i, j] be the number of paths from the start to cell (i, j).

    The robot can enter a cell only from the cell above or from the cell to the left. Every path to (i, j) therefore ends with one of those two moves, and the two groups of paths never overlap. So for an empty cell:

    dp[i, j] = dp[i - 1, j] + dp[i, j - 1]
    

    An obstacle cell has no paths through it, so its count stays 0. The first row and column are special because they have only one neighbour: each cell can be reached in exactly one way, unless an obstacle cuts the line earlier.

    For the example, the finished table is:

    1 1 1
    1 0 1
    1 1 2
    

    The bottom-right value, 2, is the answer.

    Step by step

    1. Read the size. A rectangular array has no single Length for rows or columns, so use grid.GetLength(0) for the rows (n) and grid.GetLength(1) for the columns (m).
    2. Create the table dp as an int[n, m]. C# starts every cell at 0.
    3. Fill the first column. Walk down from row 0. At the first obstacle, break: that cell and everything below it can't be reached through the column, so they keep 0. Otherwise set the cell to 1.
    4. Fill the first row the same way, walking right and stopping at the first obstacle. If the start itself is blocked, both loops break at once, and every cell stays 0.
    5. Fill the rest with two nested loops from 1, row by row. By the time a cell is reached, the cells above and to the left are done.
    6. Skip obstacles. Only when grid[i, j] == 0 is the cell set to the sum of its two neighbours.
    7. Return dp[n - 1, m - 1]. If the target is an obstacle, it was never written and the answer is 0.

    The solution in C#

    class Solution {
        public int UniquePaths(int[,] grid) {
            int n = grid.GetLength(0);
            int m = grid.GetLength(1);
            int[,] dp = new int[n, m];
            for (int i = 0; i < n; ++i) {
                if (grid[i, 0] == 1) {
                    break;
                }
                dp[i, 0] = 1;
            }
            for (int j = 0; j < m; ++j) {
                if (grid[0, j] == 1) {
                    break;
                }
                dp[0, j] = 1;
            }
            for (int i = 1; i < n; ++i) {
                for (int j = 1; j < m; ++j) {
                    if (grid[i, j] == 0) {
                        dp[i, j] = dp[i - 1, j] + dp[i, j - 1];
                    }
                }
            }
            return dp[n - 1, m - 1];
        }
    }
    

    Some C# details:

    • Two indexes in one pair of brackets. A rectangular int[,] is indexed as grid[i, j]. The jagged form grid[i][j] belongs to int[][], a different type.
    • Length counts all cells. For a 3x4 array grid.Length is 12. Use GetLength(0) and GetLength(1) for the sizes of each dimension.
    • The corner is written twice. If the start is free, both the column loop and the row loop set dp[0, 0] to 1. The second write changes nothing.
    • Zeros do the work for blocked cells. No else is needed, because the table is zero-filled and an unwritten cell already holds the right value.

    Complexity

    Time: O(n * m). Each cell is filled once with constant work.

    Space: O(n * m). The table is as big as the grid. Each row depends only on the one above, so a single row of length m could replace it.

    Edge cases and common mistakes

    • Blocked start or finish. {{1, 0}, {0, 0}} gives 0 because the robot cannot even begin, and {{0, 0}, {0, 1}} gives 0 because it cannot end.
    • A single cell. {{0}} gives 1: the robot is already at the finish. {{1}} gives 0.
    • A blocked first row or column. {{0, 1}, {0, 0}} gives 1: only the route down then right is open. Forgetting the break would let cells behind an obstacle count a path that jumps over it.
    • Treating the obstacle as 1 path. The obstacle cell must stay 0. Setting it to 1 would count paths through it.
    • Starting the main loops at 0. Row 0 and column 0 have no cell above or to the left, so the recurrence would read index -1 and throw IndexOutOfRangeException.
    • Swapping the dimensions. GetLength(0) is rows and GetLength(1) is columns. A grid with 2 rows and 3 columns has GetLength(0) equal to 2.

    Testing

    • The 3x3 grid with a center obstacle gives 2.
    • A 2x2 grid of zeros gives 2.
    • {{0, 1}, {0, 0}} gives 1.
    • {{1, 0}, {0, 0}} gives 0.
    • {{0}} gives 1.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free