Find path in a matrix of 0's and 1's - python

Recently I came across the following interview question?
Ques: You are given a 2-D matrix with M rows and N columns.You are initially positioned at (0,0) which is the top-left cell in the array. You are allowed to move either right or downwards. The array is filled with 1’s and 0’s. A 1 indicates that you can move through that cell, a 0 indicates that you cannot move through that cell. Return the number of paths from top-left cell to bottom-right cell.(i.e. (0,0)to(M-1,N-1)). Since answer can be large thus you have to return ans%(10^9+7).
Can anyone tell me how to approach or any algorithm that might help?
Edit :
I have an approach
1.Start with the top-left cell,initialize count=0
do
2.Check if 1 exists in adjacent right or adjacent down cell.
3.Add the tuple (i,j) in the stack and choose the right path.
4.If we reach the bottom right cell , update count and pop from stack and go to that position (i,j).
while(stack is not empty)
5.Print count
I was wondering if someone has some other approach?

You can model your problem as a Directed Acyclic Graph (DAG), and then you are looking for number of paths from vertex s to vertex t, which is pretty easy to do in DAG using Dynamic Programming (DP).
In here, it will be done with the following pseudo code:
D(0,0) = 1
D(x,y) = 0 if x < 0
D(x,y) = 0 if y < 0
D(x,y) = 0 if matrix[x][y] = 0
D(x,y-1) + D(x-1,y) Otherwise
By applying Dynamic Programming approach on the above, you will get a matrix, where D(x,y) indicates the number of paths from (0,0) to (x,y), and your solution is D(n,m).
Time complexity of this solution is O(n*m)
Implementing this solution is left for you, since it should be fairly easy after understanding how it is done.

Guys after all my efforts finally I am submitting my code, This prints the path positions in matrix starting from (0,0).
#!/usr/bin/env python
rows, cols = 0, 0
def traverse_matrix(A, directions, i, j, visited):
global rows, cols
def can_we_proceed(A, row, col, visited, current):
return (row >=0 and row < rows and col >=0 and col < cols and \
not visited[row][col] and (current == A[row][col]) and A[row][col] == 1)
visited[i][j] = True
for k in range(len(directions)):
if can_we_proceed(A, i+directions[k][0], j+directions[k][1], visited, A[i][j]):
print "({0},{1})".format(i+directions[k][0],j+directions[k][1])
traverse_matrix(A, directions, i+directions[k][0], j+directions[k][1], visited)
def mysolution(a):
global rows, cols
rows, cols = len(a) , len(a[0])
if (rows == 1 or cols == 1):
return 1
directions = [[1, 0], [0, -1], [-1, 0], [0, 1]]
visited = []
for i in range(rows):
l = []
for j in range(cols):
l.append(False)
visited.append(l)
# tup1 = ();
for i in range(rows):
for j in range(cols):
# tup1 = (i,j);
if not visited[i][j]:
traverse_matrix(A, directions, i, j, visited)
# print "Traversed {0};".format(tup1)

Related

Island perimeter problem - cannot count all neighbor cells

I'm working on the Island Perimeter Problem from LeetCode.
I tried to use the DFS algorithm, based on LeetCode#200, to solve this problem. For example 1, the output should be 16 but my output was 10. It seemed that not all neighbor cells were counted. Can anyone see any problems with the algorithm below?
class Solution(object):
def islandPerimeter(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
# plus 4 edges for the first cell
cnt = 4
def dfs(grid,i,j,cnt):
# return if the cell is out of the grid or equals to 0
if not 0 <= i < len(grid) or not 0 <= j < len(grid[0]) or grid[i][j] == 0: return
# put counted cell to 0 for avoiding duplicate count
grid[i][j] = 0
# plus 2 edges for each neighbor cell
cnt += 2
# search all neighbor nodes
dfs(grid,i-1,j,cnt)
dfs(grid,i+1,j,cnt)
dfs(grid,i,j-1,cnt)
dfs(grid,i,j+1,cnt)
return cnt
for i in range (len(grid)):
for j in range (len(grid[0])):
# find the first node equals to 1
if grid[i][j] == 1:
cnt += dfs(grid,i,j,cnt)
return cnt
from typing import List
# perimeter means count the edges that touches the water
class Solution:
def islandPerimeter(self,grid:List[List[int]])->int:
ROWS,COLS=len(grid),len(grid[0])
visited=set()
def dfs(r,c):
# base case we are out of bound or we reached water
if r<0 or r==ROWS or c==COLS or c<0 or grid[r][c]==0:
# that means we are on the border. so we return 1
return 1
if (r,c) in visited:
return 0
visited.add((r,c))
return dfs(r+1,c)+dfs(r-1,c)+dfs(r,c+1)+dfs(r,c-1)
# we start from land portion
for r in range(ROWS):
for c in range(COLS):
if grid[r][c]==1:
return dfs(r,c)
You can simplify your code to the following
def island_perimeter(grid):
island_size = sum([i for l in grid for i in l])
perimeter_size = island_size * 2 + 2
# correct for inner tiles
for i in range(len(grid) - 1):
for j in range(len(grid[i]) - 1):
if (grid[i][j] == 1 and grid[i+1][j] == 1
and grid[i][j+1] == 1 and grid[i+1][j+1] == 1):
perimeter_size -= 2
return perimeter_size
Given the problem description in the link you provided, both the island and the surrounding see are connect spaces. There is only one island and there are no lakes.
You can show by induction that an island of size one has a perimeter of four. And by adding a piece of island (size 1) to the some arbitrary but fixed sized island (size n) it will extend the perimeter by 2. As a correction, whenever an island of size 2x2 is created the perimeter size needs to be decreased again by two. Hence the equation given in the function above.

Python Recursion Issue (Leetcode 542)

I think I misunderstand some important concepts in Python and it is not specific to the Leetcode problem. I greatly appreciate for any help from who knows Python deeply.
The Leetcode 542 is that given a 2D array consisting of 0 and 1 only, and for each 1, find the shortest distance to reach 0. I have a dummy DFS solution:
class Solution:
def updateMatrix(self, matrix):
dists = []
for _ in range(len(matrix)):
dists.append([0]*len(matrix[0]))
for y in range(len(matrix)):
for x in range(len(matrix[0])):
if matrix[y][x] == 1:
self.min_dist = len(matrix)+len(matrix[0])
self.DFS(x, y, matrix, 0, set({}))
dists[y][x] = self.min_dist
return dists
def DFS(self, x, y, matrix, distance, visited):
if (x, y) in visited or (matrix[y][x] == 1 and distance > self.min_dist): return
if matrix[y][x] == 0:
print (x, y, "d:", distance)
print ('------')
self.min_dist = min(self.min_dist, distance)
return
print (x, y, distance)
visited.add((x, y))
if x > 0 and (x-1, y) not in visited:
self.DFS(x-1, y, matrix, distance+1, visited)
if y > 0 and (x, y-1) not in visited:
self.DFS(x, y-1, matrix, distance+1, visited)
if x < len(matrix[0])-1 and (x+1, y) not in visited:
self.DFS(x+1, y, matrix, distance+1, visited)
if y < len(matrix)-1 and (x, y+1) not in visited:
self.DFS(x, y+1, matrix, distance+1, visited)
Simply DFS until reaching 0. Every time we call DFS, distance + 1. It looks good to me. But a test case input = [[1,0,0],[0,1,1],[1,1,1]] gives me dist = [[1,0,0],[0,1,1],[1,2,3]].
If change matrix[y][x] == 1 to matrix[y][x] == 1 and x==2 and y==2 and run the above code, the output is
2 2 0
1 2 1
0 2 2
0 1 d: 3
------
1 1 2
0 1 d: 3
------
1 0 d: 3
------
2 1 3
2 0 d: 4
------
At (x,y)= (2,1) the initial distance is changed to 3. but the initial distance at (2,1) should be 1. My question is why it changes? Can anyone help me point out where I did wrong? Thanks!
You don't really need recursion for this. You can simply queue positions that need to update their neighbours and keep updating/queuing positions until no more updates are made:
def getDistances(matrix):
rows,cols = len(matrix),len(matrix[0])
maxDist = rows*cols+1 # start 1's at maximum distance
result = [[maxDist*bit for bit in row] for row in matrix]
more = { (r,c) for r,row in enumerate(matrix)
for c,bit in enumerate(row) if bit == 0}
while more: # process queue starting with zero positions
r,c = more.pop()
newDist = result[r][c]+1 # neighbours are at distance+1 from here
for nr,nc in [(r,c+1),(r,c-1),(r+1,c),(r-1,c)]: # 4 directions
if nr not in range(rows): continue
if nc not in range(cols): continue
if newDist >= result[nr][nc]: continue
result[nr][nc] = newDist # reduce neighbour's distance
more.add((nr,nc)) # queue neighbour to cascade updates
return result
output:
m = [[0,0,0,0,0,1],
[0,1,1,0,0,0],
[1,1,1,1,0,1],
[1,1,1,1,1,0]]
for r in getDistances(m): print(r)
[0, 0, 0, 0, 0, 1]
[0, 1, 1, 0, 0, 0]
[1, 2, 2, 1, 0, 1]
[2, 3, 3, 2, 1, 0]
Been taking a look at this. It seems the problem is the way the visited set is modified. I think it's being passed by reference which means by the time it tries to go (2,2) -> (2,1) the set already contains the point (2,1), i.e the preceding DFS paths have added all their points to it.
I found this article explains "Pass By Reference in Python" well - https://realpython.com/python-pass-by-reference/.
I got your test case to pass by always passing down visited.copy(), i.e self.DFS(x-1, y, matrix, distance+1, visited.copy()). I'm not a Python expert and imagine there are cleaner ways to handle this though.
First of all I want to point out that DFS, as well as BFS, is mainly used for searching in trees; indeed, you can think your matrix as a particular tree, but I wouldn't go that path for this task because you don't need to search but rather to keep track of some distance with respect to all of your neighbors, parents and children.
Moreover, with DFS you will need to traverse your matrix many times to find the minimum for every 1s and that's very inefficient.
Regarding your question, if you keep track of stack you're creating, you will get:
2 2 0
1 2 1
0 2 2
0 1 d: 3
------
back to (0, 2), with distance = 2
(1, 2) already visited
back to (1, 2) with distance = 1
1 1 2
0 1 d: 3
------
back to (1, 1) with distance = 2
1 0 d: 3
------
back to (1, 1) with distance = 2
2 1 3
2 0 d: 4
Back to your task, since you're using python I would tackle this task by using numpy, and look for 1s and 0s using np.where(matrix == 0). Then it's just a matter of doing some calculus:
import numpy as np
class Solution:
def update_matrix(self, matrix):
matrix = np.array(matrix)
x_ones, y_ones = np.where(matrix == 1)
x_zeros, y_zeros = np.where(matrix == 0)
for i in range(len(x_ones)):
temp = []
for j in range(len(x_zeros)):
temp.append(abs(x_ones[i] - x_zeros[j]) + abs(y_ones[i] - y_zeros[j]))
matrix[x_ones[i], y_ones[i]] = min(temp)
return matrix.tolist()
If you must not use external libraries, just proceed as follows:
class Solution:
def update_matrix(self, matrix):
x_ones, y_ones = [], []
x_zeros, y_zeros = [], []
# First scan to save coordinates
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == 1:
x_ones.append(i)
y_ones.append(j)
else:
x_zeros.append(i)
y_zeros.append(j)
for i in range(len(x_ones)):
temp = []
for j in range(len(x_zeros)):
temp.append(abs(x_ones[i] - x_zeros[j]) + abs(y_ones[i] - y_zeros[j]))
matrix[x_ones[i]][y_ones[i]] = min(temp)
return matrix

How do I detect if a queen is safe if I limit the queen's attack range to 5 squares?

I need to place N queen pieces on a MxM board so that no two queens attack each other. The difference from the original is that the queens can only attack up to 5 squares from their location, and not the whole row, column or diagonal as in the original problem.
One idea I'm playing around in my head is to place the first Queen on the board at board[i][j], but then also place it in its 5 neighboring squares as shown in the image below.
The following snipped is the isSafe function from the original N-Queens problem.
Looking for guidance as to how to check if a queen placement is safe.
def isSafe(board, row, col):
# Check this row on left side
for i in range(col):
if board[row][i] == 1:
return False
# Check upper diagonal on left side
for i,j in zip(range(row,-1,-1), range(col,-1,-1)):
if board[i][j] == 1:
return False
# Check lower diagonal on left side
for i,j in zip(range(row,N,1), range(col,-1,-1)):
if board[i][j] == 1:
return False
return True
In summary, how do I detect if a queen is safe if I limit the queen's range to 5 squares?
There are two main approaches:
1) For every new queen check whether another ones are at horizontals/verticals/diagonals in range. Longer to check, faster to add/remove
2) Mark beaten cells with numbers: 0 for safe one, K for the cell beaten by K queens.
In this case you can easily determine if cell is safe, increment beaten cells values for new queen and decrement them if you need to remove a queen. Faster to check, longer to add/remove.
To modify code sample, restrict ranges. For example, left-bottom diagonal:
for i,j in zip(range(row, max(-1, row - 5),-1), range(col, min(N, col + 5), 1)):
if board[i][j] == 1:
return False
Here is another approach to solve the problem.
It based on a fact that two queens can clashes only and only if they share the same row or column or diagonal, so if this condition is False, it is a safe place for the queen.
Here is an implementation :
def share_diagonal(x0, y0, x1, y1):
""" Check if two coordinates share diagonal or not ? """
dx = abs(x0 - x1)
dy = abs(y0 - y1)
return dy == dx
def col_clashes(bs, c):
""" Return True if the queen at column c clashes
with any queen to its left.
"""
for i in range(c): # Look at all columns to the left of c
if share_diagonal(i, bs[i], c, bs[c]):
return True
return False
def has_clashes(the_board):
""" Determine whether we have any queens clashing on the diagonals.
We're assuming here that the_board is a permutation of column
numbers, so we're not explicitly checking row or column clashes.
"""
for col in range(1, len(the_board)):
if col_clashes(the_board, col):
return True
return False
def main():
import random
rng = random.Random() # Instantiate a generator
bd = list(range(8)) # Generate the initial permutation
num_found = 0
tries = 0
result = []
while num_found < 10:
rng.shuffle(bd)
tries += 1
if not has_clashes(bd) and bd not in result:
print("Found solution {0} in {1} tries.".format(bd, tries))
tries = 0
num_found += 1
result.append(list(bd))
print(result)
main()

Count battleship leetcode that doesn't output the same value after changing parameters

Prompt:
Given an 2D board, count how many battleships are in it. The battleships are represented with 'X's, empty slots are represented with '.'s. You may assume the following rules:
You receive a valid board, made of only battleships or empty slots.
Battleships can only be placed horizontally or vertically. In other words, they can only be made of the shape 1xN (1 row, N columns) or Nx1 (N rows, 1 column), where N can be of any size.
At least one horizontal or vertical cell separates between two battleships - there are no adjacent battleships.
Example:
count_bs([["X",".",".","X"],[".",".",".","X"],[".",".",".","X"]])
returns 2
[X, ., ., X]
[., ., ., X]
[., ., ., X]
Solution: This code is correct.
def count_bs(grid):
rows, cols = len(grid), len(grid[0])
count = 0
for r in range(rows):
for c in range(cols):
if ((grid[r][c] == '.') or (r > 0 and grid[r-1][c] == 'X') or (c > 0 and grid[r][c-1]) == 'X'):
continue
else:
count += 1
return count
I was messing around with the 2D array and changed the parameters to see if it's correct. range(x-1) and in the conditions (r < len(grid) and grid[checks below instead of above) However, I do not get the same answer. How are these two not identical?
def count_bs(grid):
rows, cols = len(grid), len(grid[0])
count = 0
for r in range(rows-1):
for c in range(cols-1):
if ((grid[r][c] == '.') or (r < rows and grid[r+1][c] == 'X') or (c < cols and grid[r][c+1]) == 'X'):
continue
else:
count += 1
return count
So, the code above goes from top left to bottom right and checks the rows above and columns to the left to see if there are adjacent 'X' values. My code also goes from top left to bottom right, but checks the rows to the bottom and right.
Only a few small changes are needed to make your code work. You still need to loop over the entire range of rows and cols, but since you're looking at cells below and to the right of your current position, you need to change the conditions inside the loop. See here:
def count_bs(grid):
rows, cols = len(grid), len(grid[0])
count = 0
for r in range(rows):
for c in range(cols):
if ((grid[r][c] == '.')
or (r < rows-1 and grid[r+1][c] == 'X')
or (c < cols-1 and grid[r][c+1] == 'X')):
continue
else:
count += 1
return count
I only tried a few test cases but I believe this will work. As a side note, the original solution you gave is essentially counting the number of "top-left" corners of the ships, whereas your solution is counting the number of "bottom-right" corners (i.e., if we're at an X and there is not an X below us or to the right, then we must be at a bottom-right corner, so we increment the count). This might make the code a little more intuitive.

Why this DFS way not working?

Problem description:
Given a 2D grid, each cell is either a wall 'W', an enemy 'E' or empty '0' (the number zero), return the maximum enemies you can kill using one bomb.
The bomb kills all the enemies in the same row and column from the planted point until it hits the wall since the wall is too strong to be destroyed.
Note that you can only put the bomb at an empty cell.
Example:
For the given grid
0 E 0 0
E 0 W E
0 E 0 0
return 3. (Placing a bomb at (1,1) kills 3 enemies)
My DFS solution:
def maxKilledEnemies(grid):
"""
:type grid: List[List[str]]
:rtype: int
"""
l_row, l_col = len(grid), len(grid[0])
visited = [[False] * l_col for _ in range(l_row)] #using this array to avoid duplicate traverse.
def dfs(i, j):
if 0 <= i < l_row and 0 <= j < l_col and not visited[i][j]:
visited[i][j] = True
if grid[i][j] == 'W': #wall return 0
return 0
elif grid[i][j] == '0': #0 means we just ignore this cell and traverse it adjacents
top_val = dfs(i - 1, j)
down_val = dfs(i + 1, j)
left_val = dfs(i, j - 1)
right_val = dfs(i, j + 1)
return left_val + right_val + top_val + down_val
elif grid[i][j] == 'E': # Enemy and we add it by 1
top_val = dfs(i - 1, j)
down_val = dfs(i + 1, j)
left_val = dfs(i, j - 1)
right_val = dfs(i, j + 1)
return left_val + right_val + top_val + down_val + 1
return 0
ret = [0]
for i in range(l_row):
for j in range(l_col):
if not visited[i][j] and grid[i][j] == '0':
val = dfs(i, j)
ret[0] = max(val, ret[0])
return ret[0]
Solution().maxKilledEnemies([["0","E","0","0"],["E","0","W","E"],["0","E","0","0"]]) #return 4 but expect 3.
The idea is quit simple that for every cell which num is 0, we traverse it by 4
directions(Top/Down/Left/Right).
I know there are other ways to solve it more smarter. But I would like to figure out why my way not working?
There are at least three errors in your code:
In each recursion step, you explore all possible directions. This makes your search behave like a flood-fill, but you want to start in all directions from the bomb location you're checking only. After that, recurse (or search iteratively) in the given direction.
You don't check whether the possible bomb location is empty, so your code could place the bomb on an enemy.
You don't reset the visited array between searches, so that effectively only cell (0, 0) is assessed.
One solution is to have two functions,
one for the possible bomb location. Check here whether the cell is empty. If yes, count the victims by recursing north, east, south and west.
One for the "rays" of the bomb. Travel in the given direction and count the enemies until you hit a wall. Because the ray travels in only one direction, you don't need the visited array any longer.
This isn't really depth first search, so instead of calling the second function recursively, you could just use loops.
Probably because the use of DFS is not a solution. When you use DFS you search all reachable spaces in the grid but for your question you should just search the directly horizontal or vertical spaces.
Ex: When using DFS at [0][0] you will find all enemies, not only the ones at [0][1] and [1][0].

Categories