Open In App

Maximizing Safety in a Robber-Infested Grid

Last Updated : 08 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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.

DFS1

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.

DFS3

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/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++




// 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




// 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


Python3




# 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#




// 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);
    }
}


Javascript




// 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)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads