Easy Problemscountinggrid

Paths in a Grid with Obstacles

Given a grid where some cells are blocked obstacles, and starting at the top-left corner while moving only right or down at each step, count the distinct paths to the bottom-right corner that avoid every obstacle.

Do this lesson first: climbing stairs

Example input

grid = [[0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 1], [0, 1, 0, 0, 0], [1, 0, 0, 0, 0]], where 1 marks an obstacle

Expected output

17

Break it down

Answer each question out loud before you open it. Getting it wrong here is the useful part. A revealed answer you never guessed at teaches you nothing.

Fill the table

The table pauses before each cell you have to supply. Type the value the recurrence gives, and the write animation confirms it.

Step not started

Press start. The animation stops at every cell YOUR recurrence must fill.

dp[0][0] = 1 is the only base, and that single difference from the unobstructed grid is worth dwelling on. There the whole first row and first column can be declared 1 up front; here they cannot, because one obstacle in row 0 makes every cell to its right unreachable, so the edges have to be computed like every other cell, just with one candidate instead of two. Filling row-major keeps the cell above and the cell to the left final before they are used. A blocked cell is the one write in this table that depends on nothing at all: it has no cells to consult, so the walkthrough does not stop and ask you to predict it, because there is no arithmetic there to predict.

row r
0
1
2
3
4
0
1
1
1
1
1
1
1
2
0
1
2
2
1
3
3
4
0
3
1
0
3
7
7
4
0
0
3
10
17
column c: routes reaching (r, c)
paths-in-a-grid-with-obstacles.ts
  1. 1function countPaths(grid) {
  2. 2 const n = grid.length, m = grid[0].length;
  3. 3 const dp = Array.from({ length: n }, () => new Array(m).fill(0));
  4. 4 dp[0][0] = 1;
  5. 5 for (let r = 0; r < n; r++) {
  6. 6 for (let c = 0; c < m; c++) {
  7. 7 if (grid[r][c] === 1) { dp[r][c] = 0; continue; }
  8. 8 if (r === 0 && c === 0) continue;
  9. 9 const above = r > 0 ? dp[r - 1][c] : 0;
  10. 10 const left = c > 0 ? dp[r][c - 1] : 0;
  11. 11 dp[r][c] = above + left;
  12. 12 }
  13. 13 }
  14. 14 return dp[n - 1][m - 1];
  15. 15}
Base caseComputedBeing readAnswer

The code, the trap, the variations

paths-in-a-grid-with-obstacles.ts
  1. 1function countPaths(grid) {
  2. 2 const n = grid.length, m = grid[0].length;
  3. 3 const dp = Array.from({ length: n }, () => new Array(m).fill(0));
  4. 4 dp[0][0] = 1;
  5. 5 for (let r = 0; r < n; r++) {
  6. 6 for (let c = 0; c < m; c++) {
  7. 7 if (grid[r][c] === 1) { dp[r][c] = 0; continue; }
  8. 8 if (r === 0 && c === 0) continue;
  9. 9 const above = r > 0 ? dp[r - 1][c] : 0;
  10. 10 const left = c > 0 ? dp[r][c - 1] : 0;
  11. 11 dp[r][c] = above + left;
  12. 12 }
  13. 13 }
  14. 14 return dp[n - 1][m - 1];
  15. 15}

Where people go wrong

Reading every 0 in the table as an obstacle. In the 5 by 5 table, row 4 column 1 holds 0 and is perfectly walkable; both of its ways in are blocked, so no route reaches it, and it then hands that 0 on to its own neighbours like any ordinary value. Whole regions of clear cells can go dark like this behind a couple of well-placed obstacles. The other half of the trap is muscle memory on the edges: filling the first row and first column with 1s is right only until an obstacle appears in one of them, and seeding 1s past that obstacle invents routes that walk straight through a wall.

  • Ask for the cheapest route rather than the number of routes, obstacles still forbidden.

    The sum becomes this cell's cost plus a min, and a blocked cell becomes infinity rather than 0, because for a minimization 0 reads as a wonderfully cheap route while infinity is what 'no route at all' means. That is min-cost-path with the obstacle rule bolted on.

  • Remove every obstacle.

    The blocked branch never fires, row 0 and column 0 fill with 1s on their own, and the table is count-all-paths-in-a-grid again, closed form included. Drag the slider down to 2 to watch it happen: the top-left 2 by 2 corner of this grid holds no obstacle, so its answer is the unobstructed 2.

  • Allow yourself to bulldoze at most one obstacle of your choice.

    The state gains a dimension, dp[r][c][k] with k recording whether the one removal has been spent, so the same sweep runs twice over with the k = 1 layer able to step onto a blocked cell from the k = 0 layer.