Given a 2D matrix grid of size n x n, where each cell contains either a robber (denoted by 1), is empty (denoted by 0), or an obstacle (denoted by -1). Your starting position is at cell (0, 0), and you can move to any adjacent cell in the grid, including cells containing robbers but you cannot move to any cell having obstacles. The safety factor of a path on the grid is defined as the minimum Manhattan distance from any cell in the path to any robber in the grid. Your task is to find the maximum safety factor among all paths leading to cell (n - 1, n - 1) while avoiding the obstacles in the path.
Examples:
Input: grid[][] = [[0, 0, 1], [0, -1, 0], [0, 0, 0]]
Output: 2
Explanation: The path depicted has a safety factor of 2 since: The closest cell of the path to the thief at cell (0, 2) is cell (0, 0) and the obstacle is at (1, 1) which does not matter in this case. The distance between them is | 0 - 0 | + | 0 - 2 | = 2.
Input: grid[][] = [[0, 0, -1, 0], [0, 0, 0, -1], [0, -1, 0, 0], [1, 0, 0, 0]]
Output: 3
Explanation: The path depicted in the picture has a safety factor of 3 .The closest cell of the path to the robber at cell (3, 0) is cell (0, 0). The distance between them is |0 - 3| + |0 - 0| = 3. The closest cell of the path to the robber at cell (3, 0) is cell (3, 2). The distance between them is |3 - 3| + |0 - 2| = 2. The maximum safety factor among all paths leading to cell (3, 3) is 3.
Approach: To solve the problem follow the below Idea:
At first, on reading the question, the first intuition that hits our mind is to use the Breadth-First search (BFS) algorithm as we have to explore nearby cells step by step. After this step, we have to optimally find the maximum safety factor which can be obtained by using a smart search technique like Binary Search using the BFS matrix. Important thing to consider here is the obstacles which we do not have to take into consideration.
Follow the steps to solve the problem:
- Create a 2D matrix 'dist' to hold the shortest path to each cell from any cell containing a thief. Calculate the distance of the nearest cell containing a thief ('1'). Article for reference: https://www.geeksforgeeks.org/dsa/distance-nearest-cell-1-binary-matrix/
- Create a queue of 3 items - {x-coordinate, y-coordinate, steps}.
- For each cell, mark it as visited and push it into the queue with the step count as 0.
- Perform BFS accordingly and determine the distance. Update the dist matrix accordingly.
- Keep a check if there occurs any obstacles in the grid. If none occurs, then only continue the BFS traversal from that cell.
- Once the required distance is found, use the binary search to find the maximum safety factor.
- Initialize the 'low' and 'high' pointers. Perform binary search and find the mid value.
- Now again traverse through the dist matrix by performing another BFS function to check if a path with a safety factor of mid is feasible.
- If feasible, update the 'ans' variable and update low = mid + 1. If not feasible, update high = mid - 1.
- For the second BFS function, start from the top-left cell. Explore the adjacent cells and check if they satisfy the safety factor condition.
- If the bottom-right cell is reached, return true. Otherwise, return false.
Below is the implementation of the above idea:
// C++ code for the above aprpoach:
#include <bits/stdc++.h>
using namespace std;
// Function to perform BFS and check if the
// ending point is reachable with given
// safety factor
bool bfs(vector<vector<int> >& dist, int s)
{
int n = dist.size();
queue<pair<int, int> > q;
vector<vector<int> > vis(n, vector<int>(n, 0));
q.push({ 0, 0 });
vis[0][0] = 1;
// Possible moves: up, right, down, left.
int delRow[] = { -1, 0, 1, 0 };
int delCol[] = { 0, 1, 0, -1 };
while (!q.empty()) {
int r = q.front().first;
int c = q.front().second;
q.pop();
// Ending point reached
if (r == n - 1 && c == n - 1) {
return true;
}
// Traverse in all the four directions
for (int i = 0; i < 4; i++) {
int nrow = r + delRow[i];
int ncol = c + delCol[i];
// Check if the next cell is within
// the given constraints and
// appropriate conditions
if (nrow >= 0 && nrow < n && ncol >= 0
&& ncol < n && dist[nrow][ncol] >= s
&& !vis[nrow][ncol]) {
q.push({ nrow, ncol });
vis[nrow][ncol] = 1;
}
}
}
// Ending point not reachable
return false;
}
// Driver function
int main()
{
// Grid dimensions
int n = 3;
int m = 3;
vector<vector<int> > grid
= { { 0, 0, 1 }, { 0, -1, 0 }, { 0, 0, 0 } };
// Initialize the distance and
// the visited arrays
vector<vector<int> > vis(n, vector<int>(m, 0));
vector<vector<int> > dist(n, vector<int>(m, 0));
queue<pair<pair<int, int>, int> > q;
// Initialize the queue for BFS
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
vis[i][j] = 1;
q.push({ { i, j }, 0 });
}
}
}
// Possible moves: up, right, down, left.
int delRow[] = { -1, 0, 1, 0 };
int delCol[] = { 0, 1, 0, -1 };
// Perform BFS traversal to calculate
// distances from the nearest cell
// containing a robber, considering obstacles
while (!q.empty()) {
int row = q.front().first.first;
int col = q.front().first.second;
int steps = q.front().second;
q.pop();
dist[row][col] = steps;
for (int i = 0; i < 4; i++) {
int nrow = row + delRow[i];
int ncol = col + delCol[i];
// Check all the necessary conditions
if (nrow >= 0 && nrow < n && ncol >= 0
&& ncol < m && vis[nrow][ncol] == 0
&& grid[nrow][ncol] != -1) {
vis[nrow][ncol] = 1;
q.push({ { nrow, ncol }, steps + 1 });
}
}
}
// Perform binary search to find the maximum
// safeness factor, considering obstacles
int low = 0, high = 1e9, ans = 0;
while (low <= high) {
int mid = (low + high) / 2;
if (bfs(dist, mid)) {
ans = mid;
low = mid + 1;
}
else {
high = mid - 1;
}
}
// Print the maximum safety factor
cout << "Maximum Safety Factor: " << ans << endl;
return 0;
}
// Java code for the above aprpoach:
import java.util.*;
// utility pair class with generic types
class Pair<K, E> {
K first;
E second;
Pair(K f, E s)
{
first = f;
second = s;
}
}
class GFG {
// Function to perform BFS and check if the
// ending point is reachable with given
// safety factor
static boolean bfs(int[][] dist, int s)
{
int n = dist.length;
Queue<Pair<Integer, Integer> > q
= new ArrayDeque<>();
boolean[][] vis = new boolean[n][n];
q.add(new Pair<Integer, Integer>(0, 0));
vis[0][0] = true;
// Possible moves: up, right, down, left.
int delRow[] = { -1, 0, 1, 0 };
int delCol[] = { 0, 1, 0, -1 };
while (!q.isEmpty()) {
int r = q.peek().first;
int c = q.peek().second;
q.poll();
// Ending point reached
if (r == n - 1 && c == n - 1) {
return true;
}
// Traverse in all the four directions
for (int i = 0; i < 4; i++) {
int nrow = r + delRow[i];
int ncol = c + delCol[i];
// Check if the next cell is within
// the given constraints and
// appropriate conditions
if (nrow >= 0 && nrow < n && ncol >= 0
&& ncol < n && dist[nrow][ncol] >= s
&& !vis[nrow][ncol]) {
q.add(new Pair(nrow, ncol));
vis[nrow][ncol] = true;
}
}
}
// Ending point not reachable
return false;
}
// Driver function
public static void main(String[] args)
{
// Grid dimensions
int n = 3;
int m = 3;
int[][] grid
= { { 0, 0, 1 }, { 0, -1, 0 }, { 0, 0, 0 } };
// Initialize the distance and
// the visited arrays
boolean[][] vis = new boolean[n][m];
int[][] dist = new int[n][m];
Queue<Pair<Pair<Integer, Integer>, Integer> > q
= new ArrayDeque<>();
// Initialize the queue for BFS
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
vis[i][j] = true;
q.add(new Pair<Pair<Integer, Integer>,
Integer>(
new Pair<Integer, Integer>(i, j),
0));
}
}
}
// Possible moves: up, right, down, left.
int delRow[] = { -1, 0, 1, 0 };
int delCol[] = { 0, 1, 0, -1 };
// Perform BFS traversal to calculate
// distances from the nearest cell
// containing a robber, considering obstacles
while (!q.isEmpty()) {
int row = q.peek().first.first;
int col = q.peek().first.second;
int steps = q.peek().second;
q.poll();
dist[row][col] = steps;
for (int i = 0; i < 4; i++) {
int nrow = row + delRow[i];
int ncol = col + delCol[i];
// Check all the necessary conditions
if (nrow >= 0 && nrow < n && ncol >= 0
&& ncol < m && !vis[nrow][ncol]
&& grid[nrow][ncol] != -1) {
vis[nrow][ncol] = true;
q.add(new Pair<Pair<Integer, Integer>,
Integer>(
new Pair<Integer, Integer>(nrow,
ncol),
steps + 1));
}
}
}
// Perform binary search to find the maximum
// safeness factor, considering obstacles
int low = 0, high = (int)1e9, ans = 0;
while (low <= high) {
int mid = (low + high) / 2;
if (bfs(dist, mid)) {
ans = mid;
low = mid + 1;
}
else {
high = mid - 1;
}
}
// Print the maximum safety factor
System.out.println("Maximum Safety Factor: " + ans);
}
}
// This code is contributed by ragul21
# Python code for the above approach
from queue import Queue
# Function to perform BFS and check if the
# ending point is reachable with given
# safety factor
def bfs(dist, s):
n = len(dist)
q = Queue()
vis = [[0 for _ in range(n)] for _ in range(n)]
q.put((0, 0))
vis[0][0] = 1
# Possible moves: up, right, down, left.
delRow = [-1, 0, 1, 0]
delCol = [0, 1, 0, -1]
while not q.empty():
r, c = q.get()
# Ending point reached
if r == n - 1 and c == n - 1:
return True
# Traverse in all four directions
for i in range(4):
nrow = r + delRow[i]
ncol = c + delCol[i]
# Check if the next cell is within
# the given constraints and
# appropriate conditions
if 0 <= nrow < n and 0 <= ncol < n and dist[nrow][ncol] >= s and not vis[nrow][ncol]:
q.put((nrow, ncol))
vis[nrow][ncol] = 1
# Ending point not reachable
return False
# Driver function
def main():
# Grid dimensions
n = 3
m = 3
grid = [[0, 0, 1], [0, -1, 0], [0, 0, 0]]
# Initialize the distance and
# the visited arrays
vis = [[0 for _ in range(m)] for _ in range(n)]
dist = [[0 for _ in range(m)] for _ in range(n)]
q = Queue()
# Initialize the queue for BFS
for i in range(n):
for j in range(m):
if grid[i][j] == 1:
vis[i][j] = 1
q.put(((i, j), 0))
# Possible moves: up, right, down, left.
delRow = [-1, 0, 1, 0]
delCol = [0, 1, 0, -1]
# Perform BFS traversal to calculate
# distances from the nearest cell
# containing a robber, considering obstacles
while not q.empty():
(row, col), steps = q.get()
dist[row][col] = steps
for i in range(4):
nrow = row + delRow[i]
ncol = col + delCol[i]
# Check all the necessary conditions
if 0 <= nrow < n and 0 <= ncol < m and vis[nrow][ncol] == 0 and grid[nrow][ncol] != -1:
vis[nrow][ncol] = 1
q.put(((nrow, ncol), steps + 1))
# Perform binary search to find the maximum
# safeness factor, considering obstacles
low, high, ans = 0, 1e9, 0
while low <= high:
mid = (low + high) // 2
if bfs(dist, mid):
ans = mid
low = mid + 1
else:
high = mid - 1
# Print the maximum safety factor
print("Maximum Safety Factor:", ans)
if __name__ == "__main__":
main()
# This code is contributed by ragul21
// C# code for the above aprpoach
using System;
using System.Collections.Generic;
class GFG {
// Function to perform BFS and check if the
// ending point is reachable with given
// safety factor
static bool BFS(List<List<int>> dist, int s) {
int n = dist.Count;
Queue<Tuple<int, int>> q = new Queue<Tuple<int, int>>();
List<List<int>> vis = new List<List<int>>();
for (int i = 0; i < n; i++) {
vis.Add(new List<int>());
for (int j = 0; j < n; j++) {
vis[i].Add(0);
}
}
q.Enqueue(new Tuple<int, int>(0, 0));
vis[0][0] = 1;
// Possible moves: up, right, down, left.
int[] delRow = { -1, 0, 1, 0 };
int[] delCol = { 0, 1, 0, -1 };
while (q.Count != 0) {
Tuple<int, int> current = q.Dequeue();
int r = current.Item1;
int c = current.Item2;
// Ending point reached
if (r == n - 1 && c == n - 1) {
return true;
}
// Traverse in all the four directions
for (int i = 0; i < 4; i++) {
int nrow = r + delRow[i];
int ncol = c + delCol[i];
// Check if the next cell is within
// the given constraints and
// appropriate conditions
if (nrow >= 0 && nrow < n && ncol >= 0
&& ncol < n && dist[nrow][ncol] >= s
&& vis[nrow][ncol] == 0) {
q.Enqueue(new Tuple<int, int>(nrow, ncol));
vis[nrow][ncol] = 1;
}
}
}
// Ending point not reachable
return false;
}
// Driver function
static void Main(string[] args) {
// Grid dimensions
int n = 3;
int m = 3;
List<List<int>> grid = new List<List<int>> {
new List<int> { 0, 0, 1 },
new List<int> { 0, -1, 0 },
new List<int> { 0, 0, 0 }
};
// Initialize the distance and
// the visited arrays
List<List<int>> vis = new List<List<int>>();
List<List<int>> dist = new List<List<int>>();
Queue<Tuple<Tuple<int, int>, int>> q = new Queue<Tuple<Tuple<int, int>, int>>();
for (int i = 0; i < n; i++) {
vis.Add(new List<int>());
dist.Add(new List<int>());
for (int j = 0; j < m; j++) {
vis[i].Add(0);
dist[i].Add(0);
if (grid[i][j] == 1) {
vis[i][j] = 1;
q.Enqueue(new Tuple<Tuple<int, int>, int>(new Tuple<int, int>(i, j), 0));
}
}
}
// Possible moves: up, right, down, left.
int[] delRow = { -1, 0, 1, 0 };
int[] delCol = { 0, 1, 0, -1 };
// Perform BFS traversal to calculate
// distances from the nearest cell
// containing a robber, considering obstacles
while (q.Count != 0) {
Tuple<Tuple<int, int>, int> current = q.Dequeue();
int row = current.Item1.Item1;
int col = current.Item1.Item2;
int steps = current.Item2;
dist[row][col] = steps;
for (int i = 0; i < 4; i++) {
int nrow = row + delRow[i];
int ncol = col + delCol[i];
// Check all the necessary conditions
if (nrow >= 0 && nrow < n && ncol >= 0
&& ncol < m && vis[nrow][ncol] == 0
&& grid[nrow][ncol] != -1) {
vis[nrow][ncol] = 1;
q.Enqueue(new Tuple<Tuple<int, int>, int>(new Tuple<int, int>(nrow, ncol), steps + 1));
}
}
}
// Perform binary search to find the maximum
// safeness factor, considering obstacles
int low = 0, high = 1000000000, ans = 0;
while (low <= high) {
int mid = (low + high) / 2;
if (BFS(dist, mid)) {
ans = mid;
low = mid + 1;
}
else {
high = mid - 1;
}
}
// Print the maximum safety factor
Console.WriteLine("Maximum Safety Factor: " + ans);
}
}
// Function to perform BFS and check if the ending point is reachable with the given safety factor
function bfs(dist, s) {
const n = dist.length;
const vis = new Array(n).fill(null).map(() => new Array(n).fill(false));
const queue = [];
queue.push([0, 0]);
vis[0][0] = true;
// Possible moves: up, right, down, left.
const delRow = [-1, 0, 1, 0];
const delCol = [0, 1, 0, -1];
while (queue.length !== 0) {
const [r, c] = queue.shift();
// Ending point reached
if (r === n - 1 && c === n - 1) {
return true;
}
// Traverse in all four directions
for (let i = 0; i < 4; i++) {
const nrow = r + delRow[i];
const ncol = c + delCol[i];
// Check if the next cell is within the given constraints and appropriate conditions
if (nrow >= 0 && nrow < n && ncol >= 0 && ncol < n && dist[nrow][ncol] >= s && !vis[nrow][ncol]) {
queue.push([nrow, ncol]);
vis[nrow][ncol] = true;
}
}
}
// Ending point not reachable
return false;
}
// Driver function
function main() {
// Grid dimensions
const n = 3;
const m = 3;
const grid = [
[0, 0, 1],
[0, -1, 0],
[0, 0, 0]
];
// Initialize the distance and the visited arrays
const vis = new Array(n).fill(null).map(() => new Array(m).fill(false));
const dist = new Array(n).fill(null).map(() => new Array(m).fill(0));
const queue = [];
// Initialize the queue for BFS
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
if (grid[i][j] === 1) {
vis[i][j] = true;
queue.push([i, j, 0]);
}
}
}
// Possible moves: up, right, down, left.
const delRow = [-1, 0, 1, 0];
const delCol = [0, 1, 0, -1];
// Perform BFS traversal to calculate distances from the nearest cell containing a robber, considering obstacles
while (queue.length !== 0) {
const [row, col, steps] = queue.shift();
dist[row][col] = steps;
for (let i = 0; i < 4; i++) {
const nrow = row + delRow[i];
const ncol = col + delCol[i];
// Check all the necessary conditions
if (nrow >= 0 && nrow < n && ncol >= 0 && ncol < m && !vis[nrow][ncol] && grid[nrow][ncol] !== -1) {
vis[nrow][ncol] = true;
queue.push([nrow, ncol, steps + 1]);
}
}
}
// Perform binary search to find the maximum safeness factor, considering obstacles
let low = 0, high = 1e9, ans = 0;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
if (bfs(dist, mid)) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
// Print the maximum safety factor
console.log(`Maximum Safety Factor: ${ans}`);
}
// Calling the main function
main();
Output
Maximum Safety Factor: 2
Time Complexity: O(N2), Since each cell of the 2D matrix is visited once during the BFS traversal, the worst case scenario takes O(N2) time.
Auxiliary Space: O(N2), Due to the dist matrix, vis matrix, and the BFS queue, the overall space complexity of the algorithm is O(N2)

