Island perimeter problem - cannot count all neighbor cells - python

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.

Related

For number of islands problem, my code keeps exceeding max recursion depth even though it looks the same as the solution. Can someone pinpoint error?

Link to problem:
Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands.
An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
def flood(row,col):
if row<0 or col<0 or row>=len(grid) or col>=len(grid[0]) or grid[row][col]=='0':
return
grid[row][col]=='0'
flood(row+1,col)
flood(row-1,col)
flood(row,col+1)
flood(row,col-1)
return
count = 0
for row in range(len(grid)):
for col in range(len(grid[0])):
if grid[row][col]=='1':
count+=1
flood(row,col)
return count
Test case:
[["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]]
I feel my exit conditions look accurate and my numbers are quoted properly. I compared to the solution and it looks similar. The actual solution is posted below
def flood(i,j,grid):
if i< 0 or j <0 or i >=len(grid) or j>=len(grid[i]):
return
elif grid[i][j] == '0':
return
grid[i][j] = '0'
flood(i+1,j,grid)
flood(i,j+1,grid)
flood(i-1,j,grid)
flood(i,j-1,grid)
class Solution(object):
def numIslands(self, grid):
"""
:type grid: List[List[str]]
:rtype: int
"""
count = 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == '1':
count+=1
flood(i, j, grid)
return count
The only difference i see is that the grid is passed in the recursive function, which i don't think should make a difference.

Turning negative entries positive in a matrix by checking adjacent entries- only certain conversions allowed each pass, what's the time complexity?

I am given an (nxm) matrix of positive and negative integers.
The goal is to try and convert all negative numbers into positive ones, and if this is possible, return the number of passes of the matrix required to do this, otherwise return -1.
Here are the rules:
You can only convert a negative number into a positive one, if the negative number has an adjacent (directly above, below, left or right) entry that is positive.
If you convert a number from negative to positive in a certain pass, you cannot then use this (recently) turned positive number to turn other negative numbers positive- at least for this pass.
I have written an accepted solution to the problem, but I can't seem to work out the Time Complexity of the solution.
My solution:
Create a Boolean matrix to mark the negative entries that have been turned positive in the current pass (we will reset this matrix after each pass)
Iterate over all entries of the matrix
For every negative number we stumble across check all 4 of its adjacent entries and see if there are any positive entries.
If so, convert it to positive and mark it in the boolean matrix.
Increment number of passes each time we iterate over the whole matrix.
We are done when we iterate over the entire matrix and have not made a single change to it (i.e. conversion from negative to positive).
If there are any negative entries left, return -1, otherwise return the number of passes.
I can't seem to think of a worst case- any suggestions on the time complexity of this solution?
My initial thought are that it is O(n), where n is the size of the matrix.
For reference, here is my solution:
def minimumPassesOfMatrix(matrix):
numberOfPasses = 0
ChangesMade = True
while ChangesMade:
negToPosMarket = [[False for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
ChangesMade = False
for row in range(len(matrix)):
for col in range(len(matrix[0])):
if matrix[row][col] < 0:
positiveAdjacent = checkAdjacentEntriesForPositive(matrix, row, col, negToPosMarket)
if positiveAdjacent:
matrix[row][col] *= -1
negToPosMarket[row][col] = True
ChangesMade = True
if not ChangesMade:
break
numberOfPasses += 1
if all(matrix[row][col] >= 0 for row in range(len(matrix)) for col in range(len(matrix[0]))): #notebook double for loop list comp!
return numberOfPasses
print(matrix)
return -1
def checkAdjacentEntriesForPositive(matrix, row, col, negToPosMarket):
matrixHeight = len(matrix) - 1
matrixWidth = len(matrix[0]) - 1
if not OutOfBounds(row + 1, col, matrixHeight, matrixWidth) and not negToPosMarket[row + 1][col]:
if matrix[row + 1][col] > 0:
return True
if not OutOfBounds(row - 1, col, matrixHeight, matrixWidth) and not negToPosMarket[row - 1][col]:
if matrix[row - 1][col] > 0:
return True
if not OutOfBounds(row, col + 1, matrixHeight, matrixWidth) and not negToPosMarket[row][col + 1]:
if matrix[row][col + 1] > 0:
return True
if not OutOfBounds(row, col - 1, matrixHeight, matrixWidth) and not negToPosMarket[row][col - 1]:
if matrix[row][col - 1] > 0:
return True
return False
def OutOfBounds(row, col, matrixHeight, matrixWidth):
return row < 0 or col < 0 or row > matrixHeight or col > matrixWidth
The worst case scenario would be a positive number at one corner of the matrix with everything else being negative. This will require (n+m-2) passes to flip the opposite corner. The time complexity would be (n+m)xnxm if you go through every position on each pass.
If you use a set of coordinates instead, this can be reduced to nxm
Place the coordinates of positive values in a set. At each pass convert their negative neighbours to positive and place these former negative's coordinates into a new set for the next pass. This way, you only process each item once.
Here's a example in Python:
def makePos(matrix):
m = len(matrix)
n = len(matrix[0])
plusCoords = {(r,c) for r,row in enumerate(matrix)
for c,val in enumerate(row)
if val>0} # initial set of + coordinates
passes = 0
iterations = 0
while plusCoords: # more passes for new + coordinates
passes += 1 # count passes
nextCoords = set() # coordinates of new positives
for r,c in plusCoords: # go through this pass's coords
iterations += 1
for dr,dc in [(-1,0),(0,-1),(0,1),(1,0)]: # neigbors
nr,nc = r+dr, c+dc
if nr not in range(m): continue
if nc not in range(n): continue
if matrix[nr][nc] < 0: # flip to positive
nextCoords.add((nr,nc)) # track flips
for nr,nc in nextCoords:
matrix[nr][nc] *= -1 # update matrix
plusCoords = nextCoords # coords for next pass
return passes - 1, iterations
# passes
M = [ [10,-1,-1,-1,-1], # 0 1 2 3 4
[-1,-1,-1,-1,-1], # 1 2 3 4 5
[-1,-1,-1,-1,-1]] # 2 3 4 5 6
print(*makePos(M)) # 6 15 (6 passes,15 iterations)
Note that the for dr,dc in [(-1,0),(0,-1),(0,1),(1,0)]: loop counts as O(1) here because it does a fixed amount of work for the r,c coordinates. The nextCoords.add((nr,nc)) function is O(1) because nextCoords is a set.

manhattan distance heuristic in n puzzle python

I have a problem with my function that calculates the manhattan distance for the 8-puzzle in python. My code have a problem especially with a particular state: [4,3,7,5,8,6,1,0,2] ,(on some other state I tried it and it worked..) my code runs but doesn't stop running and doesn't print the result then I don't know where the problem is in my function.
The code I run:
def search(n):
s=[[4,3,7,5,8,6,1,0,2],""]
print(s)
f=frontier.create(s)
while not frontier.is_empty(f):
s=frontier.remove(f)
if state.is_target(s):
return [s, f[1], f[3]]
ns=state.get_next(s)
for i in ns:
frontier.insert(f,i)
return 0
print(search(3))
My function that calculates the Manhattan distance:
def hdistance(s):
# the heuristic value of s
i = 0 #represent the row number
j = 0 #represent the column number
sum = 0 #the manhattan distance
n = math.sqrt(len(s[0])) #calculate the size of the n puzzle
for k in s[0]: #run through the list
if k != 0:
row = int(k / n)
column = int(k % n)
sum = sum + abs(i - row) + abs(j - column)
j += 1 #increment the column
if j == n: #if I get to the end of the line
i += 1 #increment the row
j = 0
return sum #return the manhattan distance
The frontier file:
def create(s):
return [[s], 0, 0 ,0]
def is_empty(f):
return f[0]==[]
def val(s):
return state.hdistance(s)+state.path_len(s)
# Uniform Cost: return state.path_len(s)
# Greedy Best First: return state.hdistance(s)
def insert(h, s):
#inserts state s to the frontier
f=h[0]
h[1]+=1
h[2]+=1
if h[2]>h[3]:
h[3]=h[2]
f.append(s)
i=len(f)-1
while i>0 and val(f[i])<val(f[(i-1)//2]):
t=f[i]
f[i]=f[(i-1)//2]
f[(i-1)//2]=t
i=(i-1)//2
def remove(h):
if is_empty(h):
return 0
h[2]-=1
f=h[0]
s=f[0]
f[0]=f[len(f)-1]
del f[-1]
heapify(f,0)
return s
def heapify(f,i):
minSon=i
if 2*i+1<len(f) and val(f[2*i+1])<val(f[minSon]):
minSon=2*i+1
if 2*i+2<len(f) and val(f[2*i+2])<val(f[minSon]):
minSon=2*i+2
if minSon!=i:
t=f[minSon]
f[minSon]=f[i]
f[i]=t
heapify(f, minSon)
The state file:
def get_next(x): # returns a list of the children states of x
ns=[] # the next state list
for i in "<>v^":
s=x[0][:] # [:] - copies the board in x[0]
if_legal(s,i) # try to move in direction i
# checks if the move was legal and...
if s.index(0)!=x[0].index(0) and \
(x[1]=="" or x[1][-1]!="><^v"["<>v^".index(i)]): # check if it's the first move or it's a reverse move
ns.append([s,x[1]+i]) # appends the new state to ns
return ns
def path_len(x):
return len(x[1])
def is_target(x):
n=len(x[0]) # the size of the board
return x[0]==list(range(n)) # list(range(n)) is the target state
#############################
def if_legal(x,m): # gets a board and a move and makes the move if it's legal
n=int(math.sqrt(len(x))) # the size of the board is nXn
z=x.index(0) # z is the place of the empty tile (0)
if z%n>0 and m=="<": # checks if the empty tile is not in the first col and the move is to the left
x[z]=x[z-1] # swap x[z] and x[z-1]...
x[z-1]=0 # ...and move the empty tile to the left
elif z%n<n-1 and m==">": # check if the empty tile is not in the n's col and the move is to the right
x[z]=x[z+1]
x[z+1]=0
elif z>=n and m=="^": # check if the empty tile is not in the first row and the move is up
x[z]=x[z-n]
x[z-n]=0
elif z<n*n-n and m=="v": # check if the empty tile is not in the n's row and the move is down
x[z]=x[z+n]
x[z+n]=0
The rest of the code (all the other functions) are given in my assignment then the problem is certainly in my hdistance function.
If you could find where the problem is in my function, maybe there is an infinite loop...
Thank u!!

The longest path between two points from the array

I have a code that, using the algorithm of rotating calipers, defines two points with the longest distance.
The code takes in the first line the number of points N. And then N times takes the coordinates of the points X, Y. After displays the length of the longest distance.
For example:
INPUT
6
1 1
-1 0
-3 -1
-2 -2
2 3
4 -2
OUTPUT
7.0710678119
INPUT
6
2 2
0 -3
5 7
3 3
2 1
-1 1
OUTPUT
4.47213595499958 #my comment: from (3,3) to (5,7)
But there may be cases when 3 or more points are located on one straight line.
And then how should I act?
from math import *
def rast(x1, x2, y1, y2):
x = x2-x1
y = y2-y1
l = sqrt(pow(fabs(x), 2)+pow(fabs(y), 2));
return l
def orientation(p,q,r):
'''Return positive if p-q-r are clockwise, neg if ccw, zero if colinear.'''
return (q[1]-p[1])*(r[0]-p[0]) - (q[0]-p[0])*(r[1]-p[1])
def hulls(Points):
'''Graham scan to find upper and lower convex hulls of a set of 2d points.'''
U = []
L = []
Points.sort()
for p in Points:
while len(U) > 1 and orientation(U[-2],U[-1],p) <= 0: U.pop()
while len(L) > 1 and orientation(L[-2],L[-1],p) >= 0: L.pop()
U.append(p)
L.append(p)
return U,L
def rotatingCalipers(Points):
'''Given a list of 2d points, finds all ways of sandwiching the points
between two parallel lines that touch one point each, and yields the sequence
of pairs of points touched by each pair of lines.'''
U,L = hulls(Points)
i = 0
j = len(L) - 1
while i < len(U) - 1 or j > 0:
yield U[i],L[j]
# if all the way through one side of hull, advance the other side
if i == len(U) - 1: j -= 1
elif j == 0: i += 1
# still points left on both lists, compare slopes of next hull edges
# being careful to avoid divide-by-zero in slope calculation
elif (U[i+1][1]-U[i][1])*(L[j][0]-L[j-1][0]) > \
(L[j][1]-L[j-1][1])*(U[i+1][0]-U[i][0]):
i += 1
else: j -= 1
def diameter(Points):
'''Given a list of 2d points, returns the pair that's farthest apart.'''
diam,pair = max([((p[0]-q[0])**2 + (p[1]-q[1])**2, (p,q))
for p,q in rotatingCalipers(Points)])
return pair
n=int(input())
dots = []
for i in range(n):
tmp = [int(j) for j in input().split()]
dots.append([tmp[0],tmp[1]])
tmp = diameter(dots)
d1,d2=tmp[0],tmp[1]
print(rast(d1[0],d2[0],d1[1],d2[1]))
Once you have made the convex hull, you will need to sort the lines by the longest. In my example you will have the red line and after this the blue with arrow.
Take the longest line (red) and get its angle. Foreach point in the convex hull, check if the line between S and P is equal to the angle. If so calculate the distance of both lines SP & EP, if one is longer than the blue line, that is the longest line, you can stop. Else, ignore the red line and take the next longest. When there are no equal angles, you can stop.

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