LeetCodee

749. Contain Virus

Jump to Solution: Python Java C++ JavaScript C#

Problem Description

A virus is spreading rapidly, and your task is to quarantine the infected area by installing walls. The world is represented as an m x n binary grid isInfected, where isInfected[i][j] == 0 represents an uninfected cell, and isInfected[i][j] == 1 represents a cell contaminated with the virus. A wall (and only one wall) can be installed between any two 4-directionally adjacent cells, on the shared boundary.

Every night, the virus spreads to all neighboring cells in all four directions unless blocked by a wall. Resources are limited. Each day, you can install walls around only one region (i.e., the affected cells (connected 4-directionally) that share some boundary with the uninfected cells). The night after, the virus spreads to all neighboring cells in all four directions unless blocked by a wall.

Return the number of walls used to quarantine all the infected regions. If the world becomes fully infected, return the number of walls used.

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: There are 2 contaminated regions.
On the first day, add 5 walls to quarantine the viral region on the left. The board after the virus spreads is:
[[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]]
On the second day, add 5 walls to quarantine the viral region on the right. The virus is fully contained.

Example 2:

Input: isInfected = [[1,1,1],[1,0,1],[1,1,1]]
Output: 4
Explanation: Even though there is only one cell saved, there are 4 walls built.
Notice that walls are only built on the shared boundary of two different cells.

Example 3:

Input: isInfected = [[1,1,1,0,0,0,0,0,0],[1,0,1,0,1,1,1,1,1],[1,1,1,0,0,0,0,0,0]]
Output: 13
Explanation: The region on the left only builds two new walls on the first day.

Constraints:

  • m == isInfected.length
  • n == isInfected[i].length
  • 1 ≤ m, n ≤ 50
  • isInfected[i][j] is either 0 or 1
  • There is always at least one virus cell in the grid

Python Solution

class Solution:
    def containVirus(self, isInfected: List[List[int]]) -> int:
        def find_regions():
            regions = []
            seen = set()
            
            def dfs(i, j, region, frontier):
                if (i, j) in seen:
                    return
                seen.add((i, j))
                region.add((i, j))
                
                for di, dj in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
                    ni, nj = i + di, j + dj
                    if 0 <= ni < m and 0 <= nj < n:
                        if isInfected[ni][nj] == 1:
                            dfs(ni, nj, region, frontier)
                        elif isInfected[ni][nj] == 0:
                            frontier.add((ni, nj))
            
            for i in range(m):
                for j in range(n):
                    if isInfected[i][j] == 1 and (i, j) not in seen:
                        region = set()
                        frontier = set()
                        dfs(i, j, region, frontier)
                        if frontier:
                            regions.append((region, frontier))
            
            return regions
        
        def build_walls(region):
            for i, j in region:
                isInfected[i][j] = -1
                for di, dj in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
                    ni, nj = i + di, j + dj
                    if 0 <= ni < m and 0 <= nj < n and isInfected[ni][nj] == 0:
                        walls[0] += 1
        
        def spread_virus():
            for i in range(m):
                for j in range(n):
                    if isInfected[i][j] == 1:
                        for di, dj in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
                            ni, nj = i + di, j + dj
                            if 0 <= ni < m and 0 <= nj < n and isInfected[ni][nj] == 0:
                                isInfected[ni][nj] = 1
        
        m, n = len(isInfected), len(isInfected[0])
        walls = [0]
        
        while True:
            regions = find_regions()
            if not regions:
                break
                
            # Find region with largest frontier
            max_region = max(regions, key=lambda x: len(x[1]))
            build_walls(max_region[0])
            spread_virus()
        
        return walls[0]

Implementation Notes:

  • Uses DFS to find infected regions and their frontiers
  • Builds walls around the region with largest frontier each day
  • Time complexity: O(m * n * days) where days is the number of days until virus is contained
  • Space complexity: O(m * n) for storing regions and frontiers

Java Solution

class Solution {
    private int m, n;
    private int[] walls = new int[1];
    
    public int containVirus(int[][] isInfected) {
        m = isInfected.length;
        n = isInfected[0].length;
        
        while (true) {
            List regions = findRegions(isInfected);
            if (regions.isEmpty()) {
                break;
            }
            
            // Find region with largest frontier
            Region maxRegion = regions.stream()
                .max((a, b) -> a.frontier.size() - b.frontier.size())
                .get();
            
            buildWalls(isInfected, maxRegion);
            spreadVirus(isInfected);
        }
        
        return walls[0];
    }
    
    private List findRegions(int[][] isInfected) {
        List regions = new ArrayList<>();
        boolean[][] seen = new boolean[m][n];
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (isInfected[i][j] == 1 && !seen[i][j]) {
                    Region region = new Region();
                    dfs(isInfected, i, j, seen, region);
                    if (!region.frontier.isEmpty()) {
                        regions.add(region);
                    }
                }
            }
        }
        
        return regions;
    }
    
    private void dfs(int[][] isInfected, int i, int j, boolean[][] seen, Region region) {
        if (seen[i][j]) {
            return;
        }
        seen[i][j] = true;
        region.infected.add(new int[]{i, j});
        
        int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        for (int[] dir : dirs) {
            int ni = i + dir[0];
            int nj = j + dir[1];
            if (ni >= 0 && ni < m && nj >= 0 && nj < n) {
                if (isInfected[ni][nj] == 1) {
                    dfs(isInfected, ni, nj, seen, region);
                } else if (isInfected[ni][nj] == 0) {
                    region.frontier.add(new int[]{ni, nj});
                }
            }
        }
    }
    
    private void buildWalls(int[][] isInfected, Region region) {
        for (int[] cell : region.infected) {
            isInfected[cell[0]][cell[1]] = -1;
            int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
            for (int[] dir : dirs) {
                int ni = cell[0] + dir[0];
                int nj = cell[1] + dir[1];
                if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] == 0) {
                    walls[0]++;
                }
            }
        }
    }
    
    private void spreadVirus(int[][] isInfected) {
        int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (isInfected[i][j] == 1) {
                    for (int[] dir : dirs) {
                        int ni = i + dir[0];
                        int nj = j + dir[1];
                        if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] == 0) {
                            isInfected[ni][nj] = 1;
                        }
                    }
                }
            }
        }
    }
    
    private class Region {
        List infected = new ArrayList<>();
        List frontier = new ArrayList<>();
    }
}

C++ Solution

class Solution {
private:
    int m, n;
    int walls = 0;
    
    struct Region {
        vector> infected;
        vector> frontier;
    };
    
    void dfs(vector>& isInfected, int i, int j, vector>& seen, Region& region) {
        if (seen[i][j]) return;
        seen[i][j] = true;
        region.infected.emplace_back(i, j);
        
        vector> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        for (const auto& dir : dirs) {
            int ni = i + dir.first;
            int nj = j + dir.second;
            if (ni >= 0 && ni < m && nj >= 0 && nj < n) {
                if (isInfected[ni][nj] == 1) {
                    dfs(isInfected, ni, nj, seen, region);
                } else if (isInfected[ni][nj] == 0) {
                    region.frontier.emplace_back(ni, nj);
                }
            }
        }
    }
    
    vector findRegions(vector>& isInfected) {
        vector regions;
        vector> seen(m, vector(n, false));
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (isInfected[i][j] == 1 && !seen[i][j]) {
                    Region region;
                    dfs(isInfected, i, j, seen, region);
                    if (!region.frontier.empty()) {
                        regions.push_back(region);
                    }
                }
            }
        }
        
        return regions;
    }
    
    void buildWalls(vector>& isInfected, const Region& region) {
        for (const auto& cell : region.infected) {
            isInfected[cell.first][cell.second] = -1;
            vector> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
            for (const auto& dir : dirs) {
                int ni = cell.first + dir.first;
                int nj = cell.second + dir.second;
                if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] == 0) {
                    walls++;
                }
            }
        }
    }
    
    void spreadVirus(vector>& isInfected) {
        vector> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (isInfected[i][j] == 1) {
                    for (const auto& dir : dirs) {
                        int ni = i + dir.first;
                        int nj = j + dir.second;
                        if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] == 0) {
                            isInfected[ni][nj] = 1;
                        }
                    }
                }
            }
        }
    }
    
public:
    int containVirus(vector>& isInfected) {
        m = isInfected.size();
        n = isInfected[0].size();
        
        while (true) {
            auto regions = findRegions(isInfected);
            if (regions.empty()) break;
            
            // Find region with largest frontier
            auto maxRegion = max_element(regions.begin(), regions.end(),
                [](const Region& a, const Region& b) {
                    return a.frontier.size() < b.frontier.size();
                });
            
            buildWalls(isInfected, *maxRegion);
            spreadVirus(isInfected);
        }
        
        return walls;
    }
};

JavaScript Solution

function containVirus(isInfected) {
    const m = isInfected.length;
    const n = isInfected[0].length;
    let walls = 0;
    
    function findRegions() {
        const regions = [];
        const seen = new Set();
        
        function dfs(i, j, region, frontier) {
            const key = `${i},${j}`;
            if (seen.has(key)) return;
            seen.add(key);
            region.push([i, j]);
            
            const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]];
            for (const [di, dj] of dirs) {
                const ni = i + di;
                const nj = j + dj;
                if (ni >= 0 && ni < m && nj >= 0 && nj < n) {
                    if (isInfected[ni][nj] === 1) {
                        dfs(ni, nj, region, frontier);
                    } else if (isInfected[ni][nj] === 0) {
                        frontier.push([ni, nj]);
                    }
                }
            }
        }
        
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (isInfected[i][j] === 1) {
                    const region = [];
                    const frontier = [];
                    dfs(i, j, region, frontier);
                    if (frontier.length > 0) {
                        regions.push({ region, frontier });
                    }
                }
            }
        }
        
        return regions;
    }
    
    function buildWalls(region) {
        for (const [i, j] of region) {
            isInfected[i][j] = -1;
            const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]];
            for (const [di, dj] of dirs) {
                const ni = i + di;
                const nj = j + dj;
                if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] === 0) {
                    walls++;
                }
            }
        }
    }
    
    function spreadVirus() {
        const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]];
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (isInfected[i][j] === 1) {
                    for (const [di, dj] of dirs) {
                        const ni = i + di;
                        const nj = j + dj;
                        if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] === 0) {
                            isInfected[ni][nj] = 1;
                        }
                    }
                }
            }
        }
    }
    
    while (true) {
        const regions = findRegions();
        if (regions.length === 0) break;
        
        // Find region with largest frontier
        const maxRegion = regions.reduce((max, curr) => 
            curr.frontier.length > max.frontier.length ? curr : max
        );
        
        buildWalls(maxRegion.region);
        spreadVirus();
    }
    
    return walls;
}

C# Solution

public class Solution {
    private int m, n;
    private int walls = 0;
    
    private class Region {
        public List<(int i, int j)> infected = new List<(int, int)>();
        public List<(int i, int j)> frontier = new List<(int, int)>();
    }
    
    private void Dfs(int[][] isInfected, int i, int j, HashSet<(int, int)> seen, Region region) {
        if (seen.Contains((i, j))) return;
        seen.Add((i, j));
        region.infected.Add((i, j));
        
        var dirs = new[] { (0, 1), (1, 0), (0, -1), (-1, 0) };
        foreach (var dir in dirs) {
            int ni = i + dir.Item1;
            int nj = j + dir.Item2;
            if (ni >= 0 && ni < m && nj >= 0 && nj < n) {
                if (isInfected[ni][nj] == 1) {
                    Dfs(isInfected, ni, nj, seen, region);
                }
                else if (isInfected[ni][nj] == 0) {
                    region.frontier.Add((ni, nj));
                }
            }
        }
    }
    
    private List FindRegions(int[][] isInfected) {
        var regions = new List();
        var seen = new HashSet<(int, int)>();
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (isInfected[i][j] == 1 && !seen.Contains((i, j))) {
                    var region = new Region();
                    Dfs(isInfected, i, j, seen, region);
                    if (region.frontier.Count > 0) {
                        regions.Add(region);
                    }
                }
            }
        }
        
        return regions;
    }
    
    private void BuildWalls(int[][] isInfected, Region region) {
        foreach (var (i, j) in region.infected) {
            isInfected[i][j] = -1;
            var dirs = new[] { (0, 1), (1, 0), (0, -1), (-1, 0) };
            foreach (var dir in dirs) {
                int ni = i + dir.Item1;
                int nj = j + dir.Item2;
                if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] == 0) {
                    walls++;
                }
            }
        }
    }
    
    private void SpreadVirus(int[][] isInfected) {
        var dirs = new[] { (0, 1), (1, 0), (0, -1), (-1, 0) };
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (isInfected[i][j] == 1) {
                    foreach (var dir in dirs) {
                        int ni = i + dir.Item1;
                        int nj = j + dir.Item2;
                        if (ni >= 0 && ni < m && nj >= 0 && nj < n && isInfected[ni][nj] == 0) {
                            isInfected[ni][nj] = 1;
                        }
                    }
                }
            }
        }
    }
    
    public int ContainVirus(int[][] isInfected) {
        m = isInfected.Length;
        n = isInfected[0].Length;
        
        while (true) {
            var regions = FindRegions(isInfected);
            if (regions.Count == 0) break;
            
            // Find region with largest frontier
            var maxRegion = regions.OrderByDescending(r => r.frontier.Count).First();
            
            BuildWalls(isInfected, maxRegion);
            SpreadVirus(isInfected);
        }
        
        return walls;
    }
}

Implementation Notes:

  • Uses DFS to find infected regions and their frontiers
  • Builds walls around the region with largest frontier each day
  • Time complexity: O(m * n * days) where days is the number of days until virus is contained
  • Space complexity: O(m * n) for storing regions and frontiers