Open In App

Python Program for Rat in a Maze | Backtracking-2

Last Updated : 09 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

We have discussed Backtracking and Knight’s tour problem in Set 1. Let us discuss Rat in a Maze as another example problem that can be solved using Backtracking.

Consider a rat placed at (0, 0) in a square matrix of order N * N. It has to reach the destination at (N – 1, N – 1). Find all possible paths that the rat can take to reach from source to destination. The directions in which the rat can move are ‘U'(up)‘D'(down)‘L’ (left)‘R’ (right). Value 0 at a cell in the matrix represents that it is blocked and rat cannot move to it while value 1 at a cell in the matrix represents that rat can be travel through it. Return the list of paths in lexicographically increasing order.
Note: In a path, no cell can be visited more than one time. If the source cell is 0, the rat cannot move to any other cell.

Python Program for Rat in a Maze using Backtracking:

Backtracking Algorithm: Backtracking is an algorithmic technique for solving problems recursively by trying to build a solution incrementally. Solving one piece at a time, and removing those solutions that fail to satisfy the constraints of the problem at any point of time (by time, here, is referred to as the time elapsed till reaching any level of the search tree) is the process of backtracking.

Approach: 

We use a backtracking algorithm to explore all possible paths. While exploring the paths we keep track of the directions we have moved so far and when we reach to the bottom right cell, we record the path in a vector of strings.

Step-by-step approach: 

  • Instead of using a 2-D array visited[][] of size n*n to keep track of the visited cells we can simply mark the cells as blocked (maze[r] = 0) as we visit them to prevent visiting this cell again.
  • Initialize a function isValid(r, c) which returns true if the cell(r, c) is inside the maze and unblocked.
  • Start at the origin (0,0) with an empty string and try all possible paths, ie. up (U), down (D), left (L) and right (R). Since the answer must be in lexicographical order, it is better to attempt the instructions in lexicographical order ie. (D,L,R,U) and take it as string=”DLRU” to avoid sorting the list in the end.
  • If the current position (r, c) is in the bottom right corner of the grid (i == n – 1 and j == n – 1), then it means a valid top-left-to-bottom-right path was found, so add the currentPath to the ans vector and return.
  • When we are at any cell (r, c), we mark the cell as blocked (maze[r] = 0), and then explore all the possible directions to move (nextr, nextc).
  • If the next cell (nextr, nextc) is valid, then add the direction to the currentPath and move to the next cell.
  • After exploring all the paths from this cell, we can mark the current cell (r, c) again as unblocked.
  • When we have traversed all the paths, return ans.

Below is the implementation of the above approach:

Python3




class Solution:
    def __init__(self):
        # Initialize a string direction which represents all the directions.
        self.direction = "DLRU"
        # Arrays to represent changes in rows and columns
        self.dr = [1, 0, 0, -1]
        self.dc = [0, -1, 1, 0]
 
    # Function to check if cell (r, c) is inside the maze and unblocked
    def is_valid(self, r, c, n, maze):
        return 0 <= r < n and 0 <= c < n and maze[r] == 1
 
    # Function to get all valid paths
    def solve(self, r, c, maze, n, ans, current_path):
        # If we reach the bottom right cell of the matrix, add the current path to ans and return
        if r == n - 1 and c == n - 1:
            ans.append(current_path)
            return
 
        # Mark the current cell as blocked
        maze[r] = 0
 
        for i in range(4):
            # Find the next row based on the current row (r) and the dr[] array
            nextr = r + self.dr[i]
            # Find the next column based on the current column (c) and the dc[] array
            nextc = c + self.dc[i]
 
            # Check if the next cell is valid or not
            if self.is_valid(nextr, nextc, n, maze):
                current_path += self.direction[i]
                # Recursively call the solve function for the next cell
                self.solve(nextr, nextc, maze, n, ans, current_path)
                current_path = current_path[:-1]
 
        # Mark the current cell as unblocked
        maze[r] = 1
 
    def find_path(self, maze, n):
        # List to store all the valid paths
        ans = []
 
        # Check if the top left cell is unblocked
        if maze[0][0] == 1:
            current_path = ""
            self.solve(0, 0, maze, n, ans, current_path)
        return ans
 
# Main function
 
 
def main():
    n = 4
 
    m = [
        [1, 0, 0, 0],
        [1, 1, 0, 1],
        [1, 1, 0, 0],
        [0, 1, 1, 1]
    ]
 
    obj = Solution()
    result = obj.find_path(m, n)
 
    if not result:
        print(-1)
    else:
        for path in result:
            print(path, end=" ")
        print()
 
 
if __name__ == "__main__":
    main()


Output

DDRDRR DRDDRR 

Time Complexity: O(2^(n^2)), the recursion can run upper-bound 2^(n^2) times.
Auxiliary Space: O(n^2), Output matrix is required so an extra space of size n*n is needed.

Please refer to the complete article on Rat in a Maze | Backtracking-2 for more details!



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads