2617. Minimum Number of Visited Cells in a Grid
Description
You are given a 0-indexed m x n integer matrix grid. Your initial position is at the top-left cell (0, 0).
Starting from the cell (i, j), you can move to one of the following cells:
- Cells
(i, k)withj < k <= grid[i][j] + j(rightward movement), or - Cells
(k, j)withi < k <= grid[i][j] + i(downward movement).
Return the minimum number of cells you need to visit to reach the bottom-right cell (m - 1, n - 1). If there is no valid path, return -1.
Example 1:

Input: grid = [[3,4,2,1],[4,2,3,1],[2,1,0,0],[2,4,0,0]] Output: 4 Explanation: The image above shows one of the paths that visits exactly 4 cells.
Example 2:

Input: grid = [[3,4,2,1],[4,2,1,1],[2,1,1,0],[3,4,1,0]] Output: 3 Explanation: The image above shows one of the paths that visits exactly 3 cells.
Example 3:

Input: grid = [[2,1,0],[1,0,0]] Output: -1 Explanation: It can be proven that no path exists.
Constraints:
m == grid.lengthn == grid[i].length1 <= m, n <= 1051 <= m * n <= 1050 <= grid[i][j] < m * ngrid[m - 1][n - 1] == 0
Solutions
Solution: Segment Tree
- Time complexity: O(mn(log(m + n)))
- Space complexity: O(mn)
JavaScript
js
/**
* @param {number[][]} grid
* @return {number}
*/
const minimumVisitedCells = function (grid) {
const m = grid.length;
const n = grid[0].length;
const rows = Array.from({ length: m }, () => new SegmentTree(n));
const cols = Array.from({ length: n }, () => new SegmentTree(m));
rows[m - 1].update(n - 1, 1);
cols[n - 1].update(m - 1, 1);
for (let row = m - 1; row >= 0; row--) {
for (let col = n - 1; col >= 0; col--) {
const value = grid[row][col];
if (!value) continue;
const colK = Math.min(col + value, n - 1);
const rowK = Math.min(row + value, m - 1);
const moveRight = rows[row].query(col + 1, colK);
const moveDown = cols[col].query(row + 1, rowK);
const minMove = Math.min(moveRight, moveDown);
if (minMove !== Number.MAX_SAFE_INTEGER) {
const steps = minMove + 1;
rows[row].update(col, steps);
cols[col].update(row, steps);
}
}
}
const result = rows[0].query(0, 0);
return result === Number.MAX_SAFE_INTEGER ? -1 : result;
};
class SegmentTree {
constructor(n) {
this.n = n;
this.tree = new Array(n * 4).fill(Number.MAX_SAFE_INTEGER);
}
update(index, val) {
this.#update(0, 0, this.n - 1, index, val);
}
query(low, high) {
return this.#query(0, 0, this.n - 1, low, high);
}
merge(index) {
return Math.min(this.tree[index * 2 + 1], this.tree[index * 2 + 2]);
}
#update(index, left, right, target, val) {
if (left === right) {
this.tree[index] = val;
return;
}
const mid = Math.floor((left + right) / 2);
if (target <= mid) {
this.#update(index * 2 + 1, left, mid, target, val);
} else {
this.#update(index * 2 + 2, mid + 1, right, target, val);
}
this.tree[index] = this.merge(index);
}
#query(index, left, right, low, high) {
if (low <= left && high >= right) {
return this.tree[index];
}
if (low > right || high < left) {
return Number.MAX_SAFE_INTEGER;
}
const mid = Math.floor((left + right) / 2);
const leftValue = this.#query(index * 2 + 1, left, mid, low, high);
const rightValue = this.#query(index * 2 + 2, mid + 1, right, low, high);
return Math.min(leftValue, rightValue);
}
}