LogIn
I don't have account.

Contain Virus : Deep, Intuitive & Fully Explained Guide

HackFury
10 Views

#greedy

#data-structures

#depth-first-search

A virus is spreading rapidly across a grid, and your goal is to stop it by building walls around infected areas.

The grid is represented as an m x n matrix called isInfected, where each cell can either be 0 (healthy) or 1 (infected). The virus spreads every night from infected cells to their neighboring cells in all four directions up, down, left, and right unless a wall blocks the path.

You are allowed to build walls between any two adjacent cells, but only one wall can exist on the shared boundary of two cells. These walls prevent the virus from spreading across them.

However, you have limited resources. Each day, you can choose only one infected region to quarantine. A region is a continuous group of connected infected cells. Among all regions, you must pick the one that would infect the highest number of healthy cells in the next night. It is guaranteed that there will always be a clear choice with no tie.

Once you build walls around a region, it becomes fully contained and can no longer spread the virus. Meanwhile, the virus continues to spread from all other regions.

Your task is to return the total number of walls required to quarantine all the infected regions. If the virus eventually spreads to the entire grid , you should still return the total number of walls that were built until that point.

Examples

Example 1

Input
isInfected = 
[[0,1,0,0,0,0,0,1],
 [0,1,0,0,0,0,0,1],
 [0,0,0,0,0,0,0,1],
 [0,0,0,0,0,0,0,0]]

Output : 10

Explanation

Initial Observation

There are two infected regions:

  • Left region → around column 1
  • Right region → around column 7

We must choose the region that can infect more healthy cells next night.

Day 1 : Quarantine Most Dangerous Region

The left region threatens more cells, so we isolate it.

  • Walls required = 5
  • This region is now blocked completely (it will not spread anymore)

Night 1 : Virus Spreads from Remaining Region

Only the right region spreads. It infects its neighboring cells. Grid after spread becomes:


[[0,1,0,0,0,0,1,1],
 [0,1,0,0,0,0,1,1],
 [0,0,0,0,0,0,1,1],
 [0,0,0,0,0,0,0,1]]

(Left region remains unchanged because it is quarantined)

Day 2 : Quarantine Remaining Region

Now only one active region (right side) exists. Walls required to isolate it = 5

Final Result

  • Day 1 walls = 5
  • Day 2 walls = 5
  • Total walls = 10

Example 2

Input
isInfected = 
[ [1,1,1],
  [1,0,1],
  [1,1,1]]

Output : 4

Explanation

Initial Observation Here, almost the entire grid is infected except one cell in the center (1,1) which is still healthy (0).

All the surrounding cells (1s) form a single large infected region.

Day 1 : Choose Region to Quarantine

Since there is only one infected region, we must quarantine it. Now, observe carefully:

The infected region surrounds the center cell from all four directions. So, the virus can spread into the center cell from: Top, Bottom, Left, Right

To completely block this, we need to place walls on all 4 sides of the center cell. Walls required = 4

Night 1 : Virus Spread

Since the infected region is now fully quarantined, the virus cannot spread. The center cell remains safe.

Final Result

Only one day is needed:

  • Walls built = 4
  • Total walls used = 4

Step-by-Step Approach

This problem looks like a simulation, but the real trick is combining DFS + Greedy decision-making in the right way. Let’s walk through the exact approach and why each step is necessary.

Step 1: Traverse Grid & Find Regions

We scan the entire grid and run DFS (or BFS) on every unvisited infected cell (1). For each such starting point, we discover a region (a connected component of 1s). While exploring a region, we collect three important things:

  • Region cells → all infected cells belonging to this region
  • Frontier cells → all unique healthy (0) neighbors this region can infect
  • Wall count → number of walls required to block all boundaries between this region and its frontier

Key Idea : We are not just grouping cells. we are also measuring the future threat of each region.

Step 2: Evaluate Each Region

For every region, we now know:

Property Meaning
Region cells Current infected area
Frontier size Number of healthy cells it can infect next
Wall count Effort needed to isolate it

Key idea: The frontier size represents the danger level of a region.

Step 3: Pick the Most Dangerous Region

We select the region with the maximum frontier size. Why this greedy choice works:

  • This region will infect the maximum number of new cells in the next step
  • If we don’t stop it now, it will grow rapidly and become harder to control later
  • By stopping the worst region first, we minimize total future spread

This is a classic greedy strategy: Stop the biggest threat first to reduce overall damage.

Step 4: Build Walls

Add the selected region’s wall count to the final answer Mark all its cells as blocked (e.g., -1), meaning:

  • This region is permanently quarantined. It will never spread again

Why marking is important: We must ensure this region is ignored in future iterations.

Step 5: Spread Virus

Now, all the remaining regions (not quarantined) will spread. For each region, convert all its frontier cells (0) → infected (1) Why this is correct:

  • This simulates the night spread
  • Only unblocked regions are active
  • Infection grows exactly as defined in the problem

Step 6: Repeat Until No Spread Possible

We repeat the above steps until, No region has any frontier left (i.e., no more 0s to infect) or Entire grid becomes infected.


#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
    vector<vector<int>> dirs = {{1,0},{-1,0},{0,1},{0,-1}};
    
    int containVirus(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        int totalWalls = 0;
        while (true) {
            vector<vector<pair<int,int>>> regions;
            vector<unordered_set<int>> frontiers;
            vector<int> walls;
            vector<vector<bool>> visited(m, vector<bool>(n, false));
            // Step 1: Find regions
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == 1 && !visited[i][j]) {
                        vector<pair<int,int>> region;
                        unordered_set<int> frontier;
                        int wallCount = 0;
                        dfs(grid, i, j, visited, region, frontier, wallCount);
                        regions.push_back(region);
                        frontiers.push_back(frontier);
                        walls.push_back(wallCount);
                    }
                }
            }

            if (regions.empty()) break;
            // Step 2: Find most dangerous region
            int maxIdx = 0;
            for (int i = 1; i < frontiers.size(); i++) {
                if (frontiers[i].size() > frontiers[maxIdx].size()) {
                    maxIdx = i;
                }
            }
            if (frontiers[maxIdx].empty()) break;
            // Step 3: Build walls
            totalWalls += walls[maxIdx];
            // Step 4: Quarantine
            for (auto& cell : regions[maxIdx]) {
                grid[cell.first][cell.second] = -1;
            }
            // Step 5: Spread
            for (int i = 0; i < regions.size(); i++) {
                if (i == maxIdx) continue;
                for (int code : frontiers[i]) {
                    int x = code / n;
                    int y = code % n;
                    grid[x][y] = 1;
                }
            }
        }
        return totalWalls;
    }

private:
    void dfs(vector<vector<int>>& grid, int i, int j,
             vector<vector<bool>>& visited,
             vector<pair<int,int>>& region,
             unordered_set<int>& frontier,
             int& wallCount) {

        int m = grid.size(), n = grid[0].size();
        visited[i][j] = true;
        region.push_back({i,j});
        for (auto& d : dirs) {
            int x = i + d[0], y = j + d[1];
            if (x < 0 || y < 0 || x >= m || y >= n) continue;
            if (grid[x][y] == 0) {
                wallCount++;
                frontier.insert(x * n + y);
            } else if (grid[x][y] == 1 && !visited[x][y]) {
                dfs(grid, x, y, visited, region, frontier, wallCount);
            }
        }
    }
};

import java.util.*;

class Solution {
    int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
    
    public int containVirus(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int totalWalls = 0;
        while (true) {
            List<List<int[]>> regions = new ArrayList<>();
            List<Set<Integer>> frontiers = new ArrayList<>();
            List<Integer> walls = new ArrayList<>();
            boolean[][] visited = new boolean[m][n];
            // Step 1: Identify all regions
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == 1 && !visited[i][j]) {
                        List<int[]> region = new ArrayList<>();
                        Set<Integer> frontier = new HashSet<>();
                        int[] wallCount = new int[1];
                        dfs(grid, i, j, visited, region, frontier, wallCount);
                        regions.add(region);
                        frontiers.add(frontier);
                        walls.add(wallCount[0]);
                    }
                }
            }
            if (regions.isEmpty()) break;
            // Step 2: Find most dangerous region
            int maxIdx = 0;
            for (int i = 1; i < frontiers.size(); i++) {
                if (frontiers.get(i).size() > frontiers.get(maxIdx).size()) {
                    maxIdx = i;
                }
            }
            // If no spread possible
            if (frontiers.get(maxIdx).isEmpty()) break;
            // Step 3: Build walls
            totalWalls += walls.get(maxIdx);
            // Step 4: Quarantine region
            for (int[] cell : regions.get(maxIdx)) {
                grid[cell[0]][cell[1]] = -1;
            }
            // Step 5: Spread virus
            for (int i = 0; i < regions.size(); i++) {
                if (i == maxIdx) continue;
                for (int code : frontiers.get(i)) {
                    int x = code / n;
                    int y = code % n;
                    grid[x][y] = 1;
                }
            }
        }

        return totalWalls;
    }

    private void dfs(int[][] grid, int i, int j, boolean[][] visited,
                     List<int[]> region, Set<Integer> frontier, int[] wallCount) {

        int m = grid.length, n = grid[0].length;
        visited[i][j] = true;
        region.add(new int[]{i, j});
        for (int[] d : dirs) {
            int x = i + d[0], y = j + d[1];
            if (x < 0 || y < 0 || x >= m || y >= n) continue;
            if (grid[x][y] == 0) {
                wallCount[0]++;
                frontier.add(x * n + y);
            } else if (grid[x][y] == 1 && !visited[x][y]) {
                dfs(grid, x, y, visited, region, frontier, wallCount);
            }
        }
    }
}

using System;
using System.Collections.Generic;

public class Solution {
    int[][] dirs = new int[][] {
        new int[]{1,0}, new int[]{-1,0},
        new int[]{0,1}, new int[]{0,-1}
    };

    public int ContainVirus(int[][] grid) {
        int m = grid.Length, n = grid[0].Length;
        int totalWalls = 0;
        while (true) {
            var regions = new List<List<(int,int)>>();
            var frontiers = new List<HashSet<int>>();
            var walls = new List<int>();
            bool[,] visited = new bool[m, n];
            // Step 1: Find regions
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == 1 && !visited[i,j]) {
                        var region = new List<(int,int)>();
                        var frontier = new HashSet<int>();
                        int wallCount = 0;
                        DFS(grid, i, j, visited, region, frontier, ref wallCount);
                        regions.Add(region);
                        frontiers.Add(frontier);
                        walls.Add(wallCount);
                    }
                }
            }
            if (regions.Count == 0) break;
            // Step 2: Find most dangerous region
            int maxIdx = 0;
            for (int i = 1; i < frontiers.Count; i++) {
                if (frontiers[i].Count > frontiers[maxIdx].Count) {
                    maxIdx = i;
                }
            }
            if (frontiers[maxIdx].Count == 0) break;
            // Step 3: Build walls
            totalWalls += walls[maxIdx];
            // Step 4: Quarantine
            foreach (var cell in regions[maxIdx]) {
                grid[cell.Item1][cell.Item2] = -1;
            }
            // Step 5: Spread
            for (int i = 0; i < regions.Count; i++) {
                if (i == maxIdx) continue;
                foreach (int code in frontiers[i]) {
                    int x = code / n;
                    int y = code % n;
                    grid[x][y] = 1;
                }
            }
        }
        return totalWalls;
    }

    private void DFS(int[][] grid, int i, int j, bool[,] visited,
                     List<(int,int)> region, HashSet<int> frontier, ref int wallCount) {

        int m = grid.Length, n = grid[0].Length;
        visited[i,j] = true;
        region.Add((i,j));

        foreach (var d in dirs) {
            int x = i + d[0], y = j + d[1];
            if (x < 0 || y < 0 || x >= m || y >= n) continue;
            if (grid[x][y] == 0) {
                wallCount++;
                frontier.Add(x * n + y);
            } else if (grid[x][y] == 1 && !visited[x,y]) {
                DFS(grid, x, y, visited, region, frontier, ref wallCount);
            }
        }
    }
}

class Solution:
    def containVirus(self, grid):
        m, n = len(grid), len(grid[0])
        dirs = [(1,0), (-1,0), (0,1), (0,-1)]
        total_walls = 0

        def dfs(i, j, visited, region, frontier):
            stack = [(i, j)]
            visited[i][j] = True
            walls = 0
            while stack:
                x, y = stack.pop()
                region.append((x, y))
                for dx, dy in dirs:
                    nx, ny = x + dx, y + dy
                    if nx < 0 or ny < 0 or nx >= m or ny >= n:
                        continue
                    if grid[nx][ny] == 0:
                        walls += 1
                        frontier.add(nx * n + ny)
                    elif grid[nx][ny] == 1 and not visited[nx][ny]:
                        visited[nx][ny] = True
                        stack.append((nx, ny))
            return walls

        while True:
            visited = [[False]*n for _ in range(m)]
            regions, frontiers, walls = [], [], []
            # Step 1: Find regions
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == 1 and not visited[i][j]:
                        region = []
                        frontier = set()
                        wall_count = dfs(i, j, visited, region, frontier)
                        regions.append(region)
                        frontiers.append(frontier)
                        walls.append(wall_count)
            if not regions:
                break
            # Step 2: Find most dangerous region
            max_idx = max(range(len(frontiers)), key=lambda i: len(frontiers[i]))
            if len(frontiers[max_idx]) == 0:
                break
            # Step 3: Build walls
            total_walls += walls[max_idx]
            # Step 4: Quarantine
            for x, y in regions[max_idx]:
                grid[x][y] = -1
            # Step 5: Spread
            for i in range(len(regions)):
                if i == max_idx:
                    continue
                for code in frontiers[i]:
                    x, y = code // n, code % n
                    grid[x][y] = 1

        return total_walls

Complexity Analysis

Time Complexity :O((m × n)²)

  • Each iteration scans entire grid
  • Multiple iterations possible (simulation)

Space Complexity : O(m × n)

Used for:

  • Visited array
  • Region storage
  • Frontier tracking

Responses (0)

Write a response

CommentHide Comments

No Comments yet.