Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Program to count number of walls required to partition top-left and bottom-right cells in Python
In this problem, we need to find the minimum number of walls required to block all paths between the top-left cell (0,0) and bottom-right cell (R-1,C-1) in a 2D binary matrix. The matrix contains 0s (empty cells) and 1s (existing walls), and we can only move in four directions (up, down, left, right).
Problem Understanding
Given a matrix where 0 represents empty cells and 1 represents walls, we need to determine the minimum number of additional walls to place so that there's no path from top-left to bottom-right. We cannot place walls on the start and end positions.
Example
For the input matrix:
| 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 0 | 0 | 0 | 0 |
The output is 2, meaning we need to add 2 walls to block all paths:
| 0 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 0 | 0 | 1 | 0 |
Algorithm Approach
This problem uses Tarjan's algorithm to find articulation points (bridge points) in the graph. The key insight is:
- If no path exists initially, return 0
- If removing any single articulation point disconnects the graph, return 1
- Otherwise, we need 2 walls to block all paths, return 2
Implementation
from collections import deque
class Solution:
def solve(self, matrix):
R = len(matrix)
C = len(matrix[0])
def get_neighbors(i, j):
for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
if 0 <= ii < R and 0 <= jj < C and matrix[ii][jj] == 0:
yield ii, jj
visited = set()
tin = {}
low = {}
timer = 0
bridge_pts = set()
par = {}
src = (0, 0)
tgt = (R - 1, C - 1)
def dfs(v, parent):
nonlocal timer
visited.add(v)
par[v] = parent
tin[v] = timer
low[v] = timer
timer += 1
children = 0
for to in get_neighbors(*v):
if to == parent:
continue
if to in visited:
low[v] = min(low[v], tin[to])
else:
dfs(to, v)
low[v] = min(low[v], low[to])
if low[to] >= tin[v] and parent is not None:
bridge_pts.add(v)
children += 1
if parent is None and children > 1:
bridge_pts.add(v)
def bfs(root):
Q = deque([root])
visited_bfs = set([root])
while Q:
v = Q.pop()
if v == tgt:
return True
for w in get_neighbors(*v):
if w not in visited_bfs:
visited_bfs.add(w)
Q.appendleft(w)
return False
# Main algorithm
dfs(src, None)
# If target not reachable, no walls needed
if tgt not in par:
return 0
# Mark articulation points as walls
for i, j in bridge_pts:
matrix[i][j] = 1
# Check if path still exists after blocking articulation points
if bfs(src):
return 2
return 1
# Test the solution
ob = Solution()
matrix = [
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
]
print(ob.solve(matrix))
Output
2
How It Works
The algorithm works in three phases:
- DFS with Tarjan's Algorithm: Find all articulation points in the graph formed by empty cells
- Check Connectivity: If target is unreachable initially, return 0
- Test Blocking: Temporarily block articulation points and check if path still exists using BFS
Time Complexity
The time complexity is O(R × C) where R and C are the matrix dimensions, as we visit each cell at most twice during DFS and BFS traversals.
Conclusion
This solution efficiently finds the minimum walls needed by identifying critical nodes (articulation points) whose removal would disconnect the graph. The answer is always 0, 1, or 2 depending on the graph structure and connectivity.
---