Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# 3286. Find a Safe Walk Through a Grid

[LeetCode Link](https://leetcode.com/problems/find-a-safe-walk-through-a-grid/)

Difficulty: Medium
Topics: Array, Breadth-First Search, Graph Theory, Heap (Priority Queue), Matrix, Shortest Path
Acceptance Rate: 39.5%

## Hints

### Hint 1

You are moving on a grid and every step either costs you something or it doesn't. The question "can I reach the end with at least 1 health left?" is really the question "what is the *cheapest* way to reach the end?" That reframing should point you toward a **shortest-path** mindset rather than plain reachability. Think of the grid as a graph where each cell is a node and each move is an edge.

### Hint 2

What is the weight of an edge? Walking *into* a cell with `grid[i][j] = 1` costs 1 health; walking into a safe cell (`0`) costs nothing. So every edge weight is either **0 or 1**. Shortest path with only 0/1 edge weights has a specialized, linear-time algorithm — you don't need a full priority-queue Dijkstra (though that works too). Which classic technique handles 0/1 weights efficiently?

### Hint 3

Use **0-1 BFS** with a double-ended queue (deque). Maintain `dist[i][j]` = the minimum number of unsafe cells encountered on any path reaching `(i, j)` (count the starting cell's cost too). When you relax a neighbor: if the move costs 0, push it to the **front** of the deque; if it costs 1, push it to the **back**. This keeps the deque in non-decreasing order of distance, so the first time you finalize a cell you have its true minimum cost. At the end, you can survive iff `health - dist[m-1][n-1] >= 1`.

## Approach

We want the path from `(0, 0)` to `(m-1, n-1)` that passes through the fewest unsafe cells, because each unsafe cell drains exactly 1 health. If that minimum cost is `c`, then starting with `health` and requiring at least 1 health at the destination means we need `health - c >= 1`.

Model the grid as a weighted graph:

- Each cell `(i, j)` is a node.
- Moving from one cell to an adjacent cell has weight equal to the `grid` value of the cell you enter (0 for safe, 1 for unsafe).
- The starting cell `(0, 0)` also contributes its own cost, so initialize `dist[0][0] = grid[0][0]`.

Because all edge weights are 0 or 1, **0-1 BFS** finds the shortest path in `O(V + E)` time:

1. Initialize a `dist` matrix to "infinity" and set `dist[0][0] = grid[0][0]`.
2. Push the start cell into a deque.
3. Pop from the front. For each of the 4 neighbors, compute the candidate distance `dist[cur] + grid[neighbor]`.
- If it improves the neighbor's recorded distance, update it.
- Push the neighbor to the **front** of the deque if the edge cost was 0 (same distance layer), or the **back** if it was 1 (next distance layer).
4. Continue until the deque is empty.
5. Return `health - dist[m-1][n-1] >= 1`.

The deque discipline (front for 0-cost, back for 1-cost) guarantees cells are processed in non-decreasing distance order, exactly like Dijkstra but without a heap.

**Walkthrough of Example 3:** `grid = [[1,1,1],[1,0,1],[1,1,1]]`, `health = 5`. Every border cell is unsafe. The cheapest route from top-left to bottom-right that touches the single safe center cell `(1,1)` still passes through several `1`s. The minimum cost turns out to be 4, so `5 - 4 = 1 >= 1` → `true`. Any route avoiding the center costs 5, leaving `0` health → not allowed.

## Complexity Analysis

Time Complexity: O(m * n) — each cell is relaxed a constant number of times (4 neighbors), and 0-1 BFS visits each node/edge a bounded number of times.
Space Complexity: O(m * n) — for the `dist` matrix and the deque.

## Edge Cases

- **Starting cell is unsafe (`grid[0][0] = 1`):** its cost must be counted immediately; initializing `dist[0][0] = grid[0][0]` handles this.
- **Destination cell is unsafe:** counted naturally as the entry cost into `(m-1, n-1)`.
- **Health exactly equal to the minimum cost:** you'd arrive with 0 health, which is *not* allowed — the check must be `>= 1`, not `>= 0`.
- **Smallest grids (`m*n = 2`):** a 1x2 or 2x1 grid; the algorithm still works since both cells' costs are accounted for.
- **All-safe grid:** minimum cost is 0, so any positive health succeeds.
- **No 1-cost detour needed vs. forced detours:** 0-1 BFS correctly prefers 0-cost moves by pushing them to the front, ensuring the truly cheapest path is found even when a longer (in steps) but safer route exists.
83 changes: 83 additions & 0 deletions problems/3286-find-a-safe-walk-through-a-grid/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
number: "3286"
frontend_id: "3286"
title: "Find a Safe Walk Through a Grid"
slug: "find-a-safe-walk-through-a-grid"
difficulty: "Medium"
topics:
- "Array"
- "Breadth-First Search"
- "Graph Theory"
- "Heap (Priority Queue)"
- "Matrix"
- "Shortest Path"
acceptance_rate: 3945.2
is_premium: false
created_at: "2026-07-02T04:38:09.515380+00:00"
fetched_at: "2026-07-02T04:38:09.515380+00:00"
link: "https://leetcode.com/problems/find-a-safe-walk-through-a-grid/"
date: "2026-07-02"
---

# 3286. Find a Safe Walk Through a Grid

You are given an `m x n` binary matrix `grid` and an integer `health`.

You start on the upper-left corner `(0, 0)` and would like to get to the lower-right corner `(m - 1, n - 1)`.

You can move up, down, left, or right from one cell to another adjacent cell as long as your health _remains_ **positive**.

Cells `(i, j)` with `grid[i][j] = 1` are considered **unsafe** and reduce your health by 1.

Return `true` if you can reach the final cell with a health value of 1 or more, and `false` otherwise.



**Example 1:**

**Input:** grid = [[0,1,0,0,0],[0,1,0,1,0],[0,0,0,1,0]], health = 1

**Output:** true

**Explanation:**

The final cell can be reached safely by walking along the gray cells below.

![](https://assets.leetcode.com/uploads/2024/08/04/3868_examples_1drawio.png)

**Example 2:**

**Input:** grid = [[0,1,1,0,0,0],[1,0,1,0,0,0],[0,1,1,1,0,1],[0,0,1,0,1,0]], health = 3

**Output:** false

**Explanation:**

A minimum of 4 health points is needed to reach the final cell safely.

![](https://assets.leetcode.com/uploads/2024/08/04/3868_examples_2drawio.png)

**Example 3:**

**Input:** grid = [[1,1,1],[1,0,1],[1,1,1]], health = 5

**Output:** true

**Explanation:**

The final cell can be reached safely by walking along the gray cells below.

![](https://assets.leetcode.com/uploads/2024/08/04/3868_examples_3drawio.png)

Any path that does not go through the cell `(1, 1)` is unsafe since your health will drop to 0 when reaching the final cell.



**Constraints:**

* `m == grid.length`
* `n == grid[i].length`
* `1 <= m, n <= 50`
* `2 <= m * n`
* `1 <= health <= m + n`
* `grid[i][j]` is either 0 or 1.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

// Find a Safe Walk Through a Grid (LeetCode 3286)
//
// Approach: 0-1 BFS shortest path.
// Each move into a cell costs `grid[i][j]` health (0 for safe, 1 for unsafe),
// so every edge weight is 0 or 1. We compute dist[i][j] = the minimum number of
// unsafe cells on any path reaching (i, j), counting the start cell's cost.
// A deque keeps cells ordered by distance: 0-cost moves go to the front,
// 1-cost moves go to the back. We survive iff health - dist[end] >= 1.

func findSafeWalk(grid [][]int, health int) bool {
m := len(grid)
n := len(grid[0])

const inf = int(1e9)
dist := make([][]int, m)
for i := range dist {
dist[i] = make([]int, n)
for j := range dist[i] {
dist[i][j] = inf
}
}

type cell struct{ r, c int }
deque := make([]cell, 0, m*n)
dist[0][0] = grid[0][0]
deque = append(deque, cell{0, 0})

dirs := [4][2]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}

for len(deque) > 0 {
// Pop from the front.
cur := deque[0]
deque = deque[1:]

for _, d := range dirs {
nr, nc := cur.r+d[0], cur.c+d[1]
if nr < 0 || nr >= m || nc < 0 || nc >= n {
continue
}
nd := dist[cur.r][cur.c] + grid[nr][nc]
if nd < dist[nr][nc] {
dist[nr][nc] = nd
if grid[nr][nc] == 0 {
// 0-cost edge: same distance layer, push to front.
deque = append([]cell{{nr, nc}}, deque...)
} else {
// 1-cost edge: next distance layer, push to back.
deque = append(deque, cell{nr, nc})
}
}
}
}

return health-dist[m-1][n-1] >= 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import "testing"

func TestSolution(t *testing.T) {
tests := []struct {
name string
grid [][]int
health int
expected bool
}{
{
name: "example 1: reachable with health 1",
grid: [][]int{{0, 1, 0, 0, 0}, {0, 1, 0, 1, 0}, {0, 0, 0, 1, 0}},
health: 1,
expected: true,
},
{
name: "example 2: needs 4 health but only has 3",
grid: [][]int{{0, 1, 1, 0, 0, 0}, {1, 0, 1, 0, 0, 0}, {0, 1, 1, 1, 0, 1}, {0, 0, 1, 0, 1, 0}},
health: 3,
expected: false,
},
{
name: "example 3: must pass through safe center cell",
grid: [][]int{{1, 1, 1}, {1, 0, 1}, {1, 1, 1}},
health: 5,
expected: true,
},
{
name: "edge case: all-safe grid succeeds with minimal health",
grid: [][]int{{0, 0}, {0, 0}},
health: 1,
expected: true,
},
{
name: "edge case: unsafe start and end drains exactly to zero",
grid: [][]int{{1, 0}, {0, 1}},
health: 2,
expected: false,
},
{
name: "edge case: unsafe start and end survivable with extra health",
grid: [][]int{{1, 0}, {0, 1}},
health: 3,
expected: true,
},
{
name: "edge case: single row all safe",
grid: [][]int{{0, 0, 0, 0}},
health: 1,
expected: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := findSafeWalk(tt.grid, tt.health)
if result != tt.expected {
t.Errorf("findSafeWalk(%v, %d) = %v, want %v", tt.grid, tt.health, result, tt.expected)
}
})
}
}