Guys this is a question i got part of google foobar challege
You have maps of parts of the space station, each starting at a prison
exit and ending at the door to an escape pod. The map is represented
as a matrix of 0s and 1s, where 0s are passable space and 1s are
impassable walls. The door out of the prison is at the top left
(0,0)(0,0) and the door into an escape pod is at the bottom right
(w−1,h−1)(w−1,h−1).
Write a function answer(map) that generates the length of the shortest
path from the prison door to the escape pod, where you are allowed to
remove one wall as part of your remodeling plans. The path length is
the total number of nodes you pass through, counting both the entrance
and exit nodes. The starting and ending positions are always passable
(0). The map will always be solvable, though you may or may not need
to remove a wall. The height and width of the map can be from 2 to 20.
Moves can only be made in cardinal directions; no diagonal moves are
allowed.
Test cases
Input:
maze = [[0, 1, 1, 0], [0, 0, 0, 1], [1, 1, 0, 0], [1, 1, 1, 0]]
Output:
7 Input:
maze = [[0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0]] Output:
11
and this is its answer i got online
from collections import deque
class Node:
def __init__(self, x, y, saldo, grid):
self.x = x
self.y = y;
self.saldo = saldo
self.grid = grid
def __hash__(self):
return self.x ^ self.y
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.saldo == other.saldo
def get_neighbors(self):
neighbors = []
x = self.x
y = self.y
saldo = self.saldo
grid = self.grid
rows = len(grid)
columns = len(grid[0])
if x > 0:
wall = grid[y][x - 1] == 1
if wall:
if saldo > 0:
neighbors.append(Node(x - 1, y, saldo - 1, grid))
else:
neighbors.append(Node(x - 1, y, saldo, grid))
if x < columns - 1:
wall = grid[y][x + 1] == 1
if wall:
if saldo > 0:
neighbors.append(Node(x + 1, y, saldo - 1, grid))
else:
neighbors.append(Node(x + 1, y, saldo, grid))
if y > 0:
wall = grid[y - 1][x] == 1
if wall:
if saldo > 0:
neighbors.append(Node(x, y - 1, saldo - 1, grid))
else:
neighbors.append(Node(x, y - 1, saldo, grid))
if y < rows - 1:
wall = grid[y + 1][x]
if wall:
if saldo > 0:
neighbors.append(Node(x, y + 1, saldo - 1, grid))
else:
neighbors.append(Node(x, y + 1, saldo, grid))
return neighbors
def answer(maze):
rows = len(maze)
columns = len(maze[0])
source = Node(0, 0, 1, maze)
queue = deque([source])
distance_map = {source: 1}
while queue:
current_node = queue.popleft()
if current_node.x == columns - 1 and\
current_node.y == rows - 1:
return distance_map[current_node]
for child_node in current_node.get_neighbors():
if child_node not in distance_map.keys():
distance_map[child_node] = distance_map[current_node] + 1
queue.append(child_node)
and this description was there along with solution Basically you need
a breadth-first search with a minor tweak:
each node represents a cell in the grid (x- and y-coordinates), each
node knows its "saldo" (how many walls it may penetrate). What comes
to that saldo, if a node has zero saldo, it may not generate those its
neighbors that are occupied by wall. If saldo is s>0s>0, and the node
has a wall neighbor node uu, uu is generated with saldo s−1s−1.
The rest is breadth-first search: as soon you remove the exit node
from the queue, just print its distance from the starting node:
But even after a long effort i am unable to understand what "saldo"
means here and how it affects the result
i did not undertand the logic of its use
you are allowed to remove one wall as part of your remodeling plans.
Apparently, the variable saldo represents the number of walls you can remove during your escape.
It is decremented when you remove a wall; tests are made to assert whether you are still allowed to remove a wall.
Related
I have implemented a maze with python
a function addCoordinate where x and y denote the x and y coord of grid and block type: zero means open and 1 means wall.
a printMaze function which prints the maze with * for wall and empty space for open spaces.
My
Implementation seems to be working fine but could there be a better way of doing this:
class Maze:
board, max_x, max_y, list = [],2,2,[]
def __init__(self):
"""
Constructor - You may modify this, but please do not add any extra parameters
"""
def addCoordinate(self, x, y, blockType):
"""
Add information about a coordinate on the maze grid
x is the x coordinate
y is the y coordinate
blockType should be 0 (for an open space) of 1 (for a wall)
"""
if x > self.max_x:
self.max_x = x
if y > self.max_y:
self.max_y = y
if self.max_y >= len(self.board) or self.max_x >= len(self.board[0]):
newboard = [[1 for a in range(self.max_x + 1)] for b in range(self.max_y + 1)]
for i in range(len(self.board)):
for j in range(len(self.board[i])):
newboard[i][j] = self.board[i][j]
self.board = newboard
self.board[y][x] = blockType
def printMaze(self):
"""
Print out an ascii representation of the maze.
A * indicates a wall and a empty space indicates an open space in the maze
"""
for i in range(self.max_x + 1):
for j in range(self.max_y + 1):
if self.board[i][j] == 0:
print " ",
else:
print "*",
print ""
def mazeTest():
"""
This sets the open space coordinates for the example
maze in the assignment.
The remainder of coordinates within the max bounds of these specified coordinates
are assumed to be walls
"""
myMaze = Maze()
myMaze.addCoordinate(1, 0, 0)
myMaze.addCoordinate(1, 1, 0)
myMaze.addCoordinate(7, 1, 0)
myMaze.addCoordinate(1, 2, 0)
myMaze.addCoordinate(2, 2, 0)
myMaze.addCoordinate(3, 2, 0)
myMaze.addCoordinate(4, 2, 0)
myMaze.addCoordinate(6, 2, 0)
myMaze.addCoordinate(7, 2, 0)
myMaze.addCoordinate(4, 3, 0)
myMaze.addCoordinate(7, 3, 0)
myMaze.addCoordinate(4, 4, 0)
myMaze.addCoordinate(7, 4, 0)
myMaze.addCoordinate(3, 5, 0)
myMaze.addCoordinate(4, 5, 0)
myMaze.addCoordinate(7, 5, 0)
myMaze.addCoordinate(1, 6, 0)
myMaze.addCoordinate(2, 6, 0)
myMaze.addCoordinate(3, 6, 0)
myMaze.addCoordinate(4, 6, 0)
myMaze.addCoordinate(6, 6, 0)
myMaze.addCoordinate(7, 6, 0)
myMaze.addCoordinate(5, 7, 0)
myMaze.printMaze()
mazeTest()
Is there any data structures i should be using other than a list. i am looking to implement a maze solving algorithm to this maze
I want to convert [0, 0, 1, 0, 1, 0, 1, 0] to [2, 4, 6] using ortools.
Where "2", "4", "6" in the second list are the index of "1" in the first list.
Using the below code I could get a list [0, 0, 2, 0, 4, 0, 6, 0]. How can I get [2, 4, 6]?
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
work = {}
days = 8
horizon = 7
for i in range(days):
work[i] = model.NewBoolVar("work(%i)" % (i))
model.Add(work[0] == 0)
model.Add(work[1] == 0)
model.Add(work[2] == 1)
model.Add(work[3] == 0)
model.Add(work[4] == 1)
model.Add(work[5] == 0)
model.Add(work[6] == 1)
model.Add(work[7] == 0)
v1 = [model.NewIntVar(0, horizon, "") for _ in range(days)]
for d in range(days):
model.Add(v1[d] == d * work[d])
status = solver.Solve(model)
print("status:", status)
vec = []
for i in range(days):
vec.append(solver.Value(work[i]))
print("work",vec)
vec = []
for v in v1:
vec.append(solver.Value(v))
print("vec1",vec)
You should see this output on the console,
status: 4
work [0, 0, 1, 0, 1, 0, 1, 0]
vec1 [0, 0, 2, 0, 4, 0, 6, 0]
Thank you.
Edit:
I also wish to get a result as [4, 6, 2].
For just 3 variables, this is easy. In pseudo code:
The max index is max(work[i] * i)
The min index is min(horizon - (horizon - i) * work[i])
The medium is sum(i * work[i]) - max_index - min_index
But that is cheating.
If you want more that 3 variable, you will need parallel arrays of Boolean variables that indicate the rank of each variable.
Let me sketch the full solution.
You need to build a graph. The X axis are the variables. The why axis are the ranks. You have horizontal arcs going right, and diagonal arcs going right and up. If the variable is selected, you need to use a diagonal arc, otherwise an horizontal arc.
If using a diagonal arc, you will assign the current variable to the rank of the tail of the arc.
Then you need to add constraints to make it a contiguous path:
mass conservation at each node
variable is selected -> one of the diagonal arc must be selected
variable is not selected -> one of the horizontal arc must be selected
bottom left node has one outgoing arc
top right node has one incoming arc
Recently, I've found out about backtracking and without much thinking started on the book from the guy who has shown some Sudoku backtracking tricks (https://www.youtube.com/watch?v=G_UYXzGuqvM&ab_channel=Computerphile. Unfortunately, I'm stuck with the first backtracking problem without the solution.
The problem is formulated accordingly:
Use backtracking to calculate the number of all paths from the bottom left to the top right corner in a
x * y-grid. This includes paths like https://imgur.com/3t3Np4M. Note that every point can only be visited once. Write a function np(x,y) that returns the number of paths in a x*y-grid. E.g. np(2,3) should return 38. Hint: Create a grid of booleans where you mark the positions already visited.
Whatever I change in this short block of code I'm landing nowhere near 38.
```
grid = [[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0]]
solution = 0
def number_of_paths(x, y):
global solution
global grid
for i in range(0, x):
for j in range(0, y):
if grid[i][j] == 0:
grid[i][j] = 1
number_of_paths(x, y)
grid[i][j] = 0
solution += 1
return
if __name__ == '__main__':
number_of_paths(2, 3)
print(grid)
print(solution)```
That's a sample solution with solution with Sudoku solver.
```
grid = [[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9]]
import numpy as np
def possible(y, x, n):
global grid
for i in range(0, 9):
if grid[y][i] == n:
return False
for i in range(0, 9):
if grid[i][x] == n:
return False
x0 = (x // 3) * 3
y0 = (y // 3) * 3
for i in range(0, 3):
for j in range(0, 3):
if grid[y0 + i][x0 + j] == n:
return False
return True
def solve():
global grid
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y, x, n):
grid[y][x] = n
solve()
# backtracking - bad choice
grid[y][x] = 0
return
print(np,matrix(grid))
input("More?")```
A few suggestions:
You might want to use a set for a grid, adding a square as soon as it is visited, if it is not a member of the set yet.
The counter and the grid can be global but it would probably be easier for you to take them as arguments for the function at first. After the solution is clearer you can worry about those details.
You are going about the problem the wrong way. It would be good to have one function calculating the number of paths from the origin to the destination (by calling the function for the neighbors that have not been visited yet. Make sure you update the grid). On top of that you can have a function that calls the path function for every combination of origin and destination. A small tip: You do not have to calculate the same path in reverse direction! You can have a map of calculate sums of paths. If the opposite direction has been calculate, don't bother. Later, double the amount of paths by 2.
Good luck!
I will show you a solution on a coordinate system where (0,0) is the topleft and (maxY,maxX) is the bot right. Going right increases x and going down increases y.
1- If you are trying to solve the exact maze in the image, then your grid array shape is wrong. Notice that you are travelling between corners of the squares, there are 4 points you can be horizontally and 3 points you can be vertically.
2- Hint is telling you about using a boolean mask for visited state, you already have a grid array so a separate array is not necessary.
3- The main problem with your code is how you are progressing in the maze. The loop structure
for i in range(0, x):
for j in range(0, y):
does not make sense because when you are in a position (x, y), you can only move in 4 main directions (right, up, left, down). However this loops make it look like you are trying to branch into all positions behind you, which is not valid. In my code I will explicity show about this traverse stuff.
grid = [[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 0, 0, 0]]
# number of solutions
solution = 0
# maximum values of x and y coordinates
maxX = len(grid[0])-1
maxY = len(grid)-1
# endpoint coordinates, top(y=0) right(x=maxX) of the maze
endX = maxX
endY = 0
# starting point coordinates, bottom(y=maxY) left(x=0) of the maze
mazeStartX = 0
mazeStartY = maxY
def number_of_paths(startX, startY):
global solution
global grid
global mask
# if we reached the goal, return at this point
if (startX == endX and startY == endY):
solution += 1
return
# possible directions are
#RIGHT (+1x, 0y)
#UP (0x, -1y)
#LEFT (-1x, 0y)
#DOWN (0x, +1y)
# I use a direction array like this to avoid nested ifs inside the for loop
dx = [1, 0, -1, 0]
dy = [0, -1, 0, 1]
for d in range(len(dx)):
newX = startX + dx[d]
newY = startY + dy[d]
# out of maze bounds
if (newX < 0 or newY < 0):
continue
# out of maze bounds
if (newX > maxX or newY > maxY):
continue
if (grid[newY][newX] == 1):
# this are is already visited
continue
else:
# branch from this point
grid[newY][newX] = 1
number_of_paths(newX, newY)
grid[newY][newX] = 0
if __name__ == '__main__':
number_of_paths(mazeStartX, mazeStartY)
print(grid)
print(solution)
I am trying to make a function that given a 2d array of either 0's or 1's and a set of coordinates, returns the area of the selected region with the same value as the given coordinates.
For example given the array:
[[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0]]
and the coordinates [0,0], it would return 5.
I have tried a simple DFS but am running into issues where it runs over the same spots multiple times, returning an abnormally large area like 330.
I actually figured this one out!
Basically it follows a BFS search looking only up, down, left, and right.
The part on finding the neighbours was taken from this answer to another question.
def find_area(arr, x, y):
queue = [(x,y)]
visited = []
w = len(arr[0])
h = len(arr)
area = 0
while len(queue) != 0:
curr = queue.pop(0)
print(curr)
x = curr[0]
y = curr[1]
if arr[curr[0]][curr[1]] == 0 and curr not in visited:
area += 1
visited.append(curr)
neighbors = [(x+a[0], y+a[1])
for a in [(-1,0), (1,0), (0,-1), (0,1)]
if ((0 <= x+a[0] < w) and (0 <= y+a[1] < h))]
for i in neighbors:
if i not in visited:
queue.append(i)
return area
I need to generate an iterator which will iterate over a Python 2D array and yield each item and all the items around it in an MxN neighbourhood.
For instance, given a list of 0s and 1s in a checkerboard pattern, I need an iterator object that would yield a 3x3 neighbourhood such as:
[0,1,0],
[1,0,1],
[0,1,0]
N.B. The yield does not need to be another array, but it would be nice to be able to refer to neighbours with positions/indexes relative to the central item, or at least relative to each other.
Thanks in advance.
Edit: So far I have been trying to do it solely by index, i.e.
for x in range(len(S)):
for y in range(len(S[0])):
for i in range(-1,2):
for j in range(-1,2):
#access neighbour with S[x+i][y+j]
board = [
[1,0,1,0,1],
[1,0,1,0,1],
[1,0,1,0,1],
[1,0,1,0,1],
[1,0,1,0,1]
]
def clamp(minV,maxV,x):
if x < minV:
return minV
elif x > maxV:
return maxV
else:
return x
def getNeighbour(grid,startx,starty,radius):
width = len(grid[starty])
height = len(grid)
neighbourhood = []
for y in range(clamp(0,height,starty-radius),clamp(0,height,starty+radius)+1):
row = []
for x in range(clamp(0,width,startx-radius),clamp(0,width,startx+radius)+1):
if x != startx or (x==startx and y != starty):
row.append(grid[y][x])
neighbourhood.append(row)
return neighbourhood
Examples:
>>> pprint(getNeighbour(board, 0, 0, 1))
[0]
[1, 0] (expected)
>>> pprint(getNeighbour(board, 2, 2, 1))
[0, 1, 0]
[0, 0]
[0, 1, 0] (expected)
>>>
Addressing the performance aspect with a list like:
board = [[1,0]*2000]*1000
The run time is essentially the same as if the board were 10x10
You can often get fast access of 2 dimensional array by storing the elements in a one dimensional list and computing offset bases on the logical width and height of array. It can often simplify calculations and bounds-checking, which only have to deal with one dimension internally.
class Board(object):
def __init__(self, width, height):
self.height = height
self.width = width
self.size = width*height
self.board = [i%2 for i in xrange(self.size)] # checkerboard init
def __getitem__(coords):
""" get board[x, y] """
offset = coords[1]*self.width + coords[0]
return self.board[offset]
def __setitem__(coords, value):
""" set board[x, y] = value """
offset = coords[1]*self.width + coords[0]
self.board[offset] = value
def __str__(self):
lines = []
for y in xrange(self.height):
offset = y*self.width
row = self.board[offset:offset+self.width]
lines.append(','.join(str(v) for v in row))
return ',\n'.join(lines)
def neighbourhood(self, x, y):
position = y*self.width + x
for offset in [
position-self.width-1, position-self.width, position-self.width+1,
position-1, position+1,
position+self.width-1, position+self.width, position+self.width+1]:
if -1 < offset < self.size:
yield self.board[offset]
board = Board(5, 5)
print board
print
print [value for value in board.neighbourhood(0, 0)]
print [value for value in board.neighbourhood(2, 2)]
Output:
0,1,0,1,0,
1,0,1,0,1,
0,1,0,1,0,
1,0,1,0,1,
0,1,0,1,0
[1, 0, 1, 0]
[0, 1, 0, 1, 1, 0, 1, 0]