diff --git a/problems/3286-find-a-safe-walk-through-a-grid/analysis_daily_20260702.md b/problems/3286-find-a-safe-walk-through-a-grid/analysis_daily_20260702.md new file mode 100644 index 0000000..449bbe2 --- /dev/null +++ b/problems/3286-find-a-safe-walk-through-a-grid/analysis_daily_20260702.md @@ -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. diff --git a/problems/3286-find-a-safe-walk-through-a-grid/problem.md b/problems/3286-find-a-safe-walk-through-a-grid/problem.md new file mode 100644 index 0000000..ec5f5ec --- /dev/null +++ b/problems/3286-find-a-safe-walk-through-a-grid/problem.md @@ -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. diff --git a/problems/3286-find-a-safe-walk-through-a-grid/solution_daily_20260702.go b/problems/3286-find-a-safe-walk-through-a-grid/solution_daily_20260702.go new file mode 100644 index 0000000..6e8a674 --- /dev/null +++ b/problems/3286-find-a-safe-walk-through-a-grid/solution_daily_20260702.go @@ -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 +} diff --git a/problems/3286-find-a-safe-walk-through-a-grid/solution_daily_20260702_test.go b/problems/3286-find-a-safe-walk-through-a-grid/solution_daily_20260702_test.go new file mode 100644 index 0000000..edb9805 --- /dev/null +++ b/problems/3286-find-a-safe-walk-through-a-grid/solution_daily_20260702_test.go @@ -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) + } + }) + } +}