Islands and Treasure
Problem
You are given a m×nm×n 2D grid initialized with these three possible values:
-1- A water cell that can not be traversed.0- A treasure chest.INF- A land cell that can be traversed. We use the integer2^31 - 1 = 2147483647to representINF.
Fill each land cell with the distance to its nearest treasure chest. If a land cell cannot reach a treasure chest then the value should remain INF.
Assume the grid can only be traversed up, down, left, or right.
Modify the grid in-place.
Examples
Example 1:
Input: [
[2147483647,-1,0,2147483647],
[2147483647,2147483647,2147483647,-1],
[2147483647,-1,2147483647,-1],
[0,-1,2147483647,2147483647]
]
Output: [
[3,-1,0,1],
[2,2,1,-1],
[1,-1,2,-1],
[0,-1,3,4]
]
Example 2:
Input: [
[0,-1],
[2147483647,2147483647]
]
Output: [
[0,-1],
[1,2]
]
Constraints
m == grid.lengthn == grid[i].length1 <= m, n <= 100grid[i][j]is one of{-1, 0, 2147483647}
You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given grid.
Solution
Perform BFS by adding all chests to open. Prune if already assigned a value, as we are guaranteed to visit land in shortest path.
class Solution {
inline static std::array<std::pair<int, int>, 4> OFFSETS = {{
{-1, 0}, {1, 0}, {0, -1}, {0, 1}
}};
public:
void islandsAndTreasure(vector<vector<int>>& grid) {
const int WATER = -1;
const int CHEST = 0;
const int LAND = 2147483647;
const int ROWS = grid.size();
const int COLS = grid[0].size();
std::queue<std::pair<int, int>> open;
// CHESTS all start at level 0
for (int r = 0; r < ROWS; ++r) {
for (int c = 0; c < COLS; ++c) {
if (grid[r][c] == CHEST) {
open.emplace(r, c);
}
}
}
// BFS
while (!open.empty()) {
auto [row, col] = open.front();
open.pop();
for (const auto [dr, dc] : OFFSETS) {
auto r = row + dr;
auto c = col + dc;
// Out of bounds
if (r < 0 || c < 0 || r >= ROWS || c >= COLS) {
continue;
}
// Already assigned shortest path
if (grid[r][c] != LAND) { continue; }
grid[r][c] = grid[row][col] + 1;
open.emplace(r, c);
}
}
}
};
Starting at a chest cell, flood fill out counting depth. If we come across a LAND cell with lower cost, stop.
class Solution {
public:
void islandsAndTreasure(vector<vector<int>>& grid) {
const int WATER = -1;
const int CHEST = 0;
const int LAND = 2147483647;
const int ROWS = grid.size();
const int COLS = grid[0].size();
std::function<void(int, int, int)> dfs;
dfs = [&](int r, int c, int d) {
// Out of bounds check
if (r < 0 || c < 0 || r >= ROWS || c >= COLS) {
return;
}
// If non-land, skip (edge case of starting at chest)
if (d > 0 && (grid[r][c] == CHEST || grid[r][c] == WATER) ) {
return;
}
// Land cell may be reachable by more than one chest
// Ensure we track closest one
// Better path already found, don't continue
if (grid[r][c] < d) { return; }
// Better path found, flood fill to neighbors
grid[r][c] = d;
dfs(r - 1, c, d + 1);
dfs(r + 1, c, d + 1);
dfs(r, c - 1, d + 1);
dfs(r, c + 1, d + 1);
};
// Loop until we find a chest
// Perform floodfill away from chest, counting
for (int r = 0; r < ROWS; ++r) {
for (int c = 0; c < COLS; ++c) {
if (grid[r][c] == CHEST) {
dfs(r, c, 0);
}
}
}
}
};