Open In App
Related Articles

Rat in a Maze

Improve Article
Improve
Save Article
Save
Like Article
Like

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.

Example:

Input:

Output: DRDDRR
Explanation:

Rat in a Maze using Backtracking:

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:

  • Create isValid() function to check if a cell at position (r, c) is inside the maze and unblocked.
  • Create findPath() to get all valid paths:
    • Base case: If the current position is the bottom-right cell, add the current path to the result and return.
    • Mark the current cell as blocked.
    • Iterate through all possible directions.
      • Calculate the next position based on the current direction.
      • If the next position is valid (i.e, if isValid() return true), append the direction to the current path and recursively call the findPath() function for the next cell.
      • Backtrack by removing the last direction from the current path.
  • Mark the current cell as unblocked before returning.

Below is the implementation of the above approach:

C++




#include <bits/stdc++.h>
using namespace std;
 
// initialize a string direction which represents all
// the directions.
string direction = "DLRU";
// arrays to represent change in rows and columns
 
int dr[4] = { 1, 0, 0, -1 };
int dc[4] = { 0, -1, 1, 0 };
 
// Function to check if cell(r, c) is inside the maze
// and unblocked
bool isValid(int r, int c, int n,
             vector<vector<int> >& maze)
{
    return r >= 0 && c >= 0 && r < n && c < n && maze[r];
}
 
// Function to get all valid paths
void findPath(int r, int c, vector<vector<int> >& maze,
              int n, vector<string>& ans,
              string& currentPath)
{
 
    // if we reach the bottom right cell of the matrix,
    // add the current path to ans and return
    if (r == n - 1 && c == n - 1) {
        ans.push_back(currentPath);
        return;
    }
 
    // Mark the current cell as blocked
    maze[r] = 0;
 
    for (int i = 0; i < 4; i++) {
        // find the next row based on the current row
        // (r) and the dr[] array
        int nextr = r + dr[i];
        // find the next column based on the current
        // column (c) and the dc[] array
        int nextc = c + dc[i];
        // check if the next cell is valid or not
        if (isValid(nextr, nextc, n, maze)) {
            currentPath += direction[i];
            // Recursively call the solve function for
            // the next cell
            findPath(nextr, nextc, maze, n, ans,
                     currentPath);
            currentPath.pop_back();
        }
    }
    // Mark the current cell as unblocked
    maze[r] = 1;
}
 
int main()
{
    int n = 4;
 
    vector<vector<int> > maze = { { 1, 0, 0, 0 },
                                  { 1, 1, 0, 1 },
                                  { 1, 1, 0, 0 },
                                  { 0, 1, 1, 1 } };
 
    // vector to store all the valid paths
    vector<string> result;
 
    // Store current Path
    string currentPath = "";
 
    // Function call to get all valid paths
    findPath(0, 0, maze, n, result, currentPath);
 
    if (result.size() == 0)
        cout << -1;
    else
        for (int i = 0; i < result.size(); i++)
            cout << result[i] << " ";
    cout << endl;
 
    return 0;
}


Java




import java.util.ArrayList;
import java.util.List;
 
public class MazeSolver {
 
    // Initialize a string direction which represents all
    // the directions.
    private static final String direction = "DLRU";
    // Arrays to represent change in rows and columns
    private static final int[] dr = { 1, 0, 0, -1 };
    private static final int[] dc = { 0, -1, 1, 0 };
 
    // Function to check if cell(r, c) is inside the maze
    // and unblocked
    private static boolean isValid(int r, int c, int n,
                                   int[][] maze)
    {
        return r >= 0 && c >= 0 && r < n && c < n
            && maze[r] == 1;
    }
 
    // Function to get all valid paths
    private static void findPath(int r, int c, int[][] maze,
                                 int n, List<String> ans,
                                 StringBuilder currentPath)
    {
 
        // If we reach the bottom right cell of the matrix,
        // add the current path to ans and return
        if (r == n - 1 && c == n - 1) {
            ans.add(currentPath.toString());
            return;
        }
 
        // Mark the current cell as blocked
        maze[r] = 0;
 
        for (int i = 0; i < 4; i++) {
            // Find the next row based on the current row
            // (r) and the dr[] array
            int nextr = r + dr[i];
            // Find the next column based on the current
            // column (c) and the dc[] array
            int nextc = c + dc[i];
            // Check if the next cell is valid or not
            if (isValid(nextr, nextc, n, maze)) {
                currentPath.append(direction.charAt(i));
                // Recursively call the solve function for
                // the next cell
                findPath(nextr, nextc, maze, n, ans,
                         currentPath);
                currentPath.deleteCharAt(
                    currentPath.length() - 1);
            }
        }
        // Mark the current cell as unblocked
        maze[r] = 1;
    }
 
    public static void main(String[] args)
    {
        int n = 4;
 
        int[][] maze = { { 1, 0, 0, 0 },
                         { 1, 1, 0, 1 },
                         { 1, 1, 0, 0 },
                         { 0, 1, 1, 1 } };
 
        // List to store all the valid paths
        List<String> result = new ArrayList<>();
 
        // Store current Path
        StringBuilder currentPath = new StringBuilder();
 
        // Function call to get all valid paths
        findPath(0, 0, maze, n, result, currentPath);
 
        if (result.isEmpty())
            System.out.println(-1);
        else
            for (String path : result)
                System.out.print(path + " ");
        System.out.println();
    }
}


Python3




direction = "DLRU"
dr = [1, 0, 0, -1]
dc = [0, -1, 1, 0]
 
 
def is_valid(r, c, n, maze):
    return 0 <= r < n and 0 <= c < n and maze[r]
 
 
def findPath(r, c, maze, n, ans, current_path):
    # Base case: if we reach the bottom-right cell, 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 and column based on the current row and column
        nextr = r + dr[i]
        nextc = c + dc[i]
        # Check if the next cell is valid
        if is_valid(nextr, nextc, n, maze):
            current_path += direction[i]
            # Recursively call the find_path function for the next cell
            findPath(nextr, nextc, maze, n, ans, current_path)
            # Backtrack by removing the last direction
            current_path = current_path[:-1]
 
    # Mark the current cell as unblocked
    maze[r] = 1
 
 
if __name__ == "__main__":
    n = 4
    maze = [
        [1, 0, 0, 0],
        [1, 1, 0, 1],
        [1, 1, 0, 0],
        [0, 1, 1, 1]
    ]
 
    # List to store all valid paths
    result = []
 
    # Store current path
    current_path = ""
 
    # Function call to get all valid paths
    findPath(0, 0, maze, n, result, current_path)
 
    if len(result) == 0:
        print(-1)
    else:
        for path in result:
            print(path, end=" ")
        print()


C#




using System;
using System.Collections.Generic;
 
class Program
{
    // Initialize a string direction which represents all the directions.
    static string direction = "DLRU";
 
    // Arrays to represent change in rows and columns
    static int[] dr = { 1, 0, 0, -1 };
    static int[] dc = { 0, -1, 1, 0 };
 
    // Function to check if cell(r, c) is inside the maze and unblocked
    static bool IsValid(int r, int c, int n, int[,] maze)
    {
        return r >= 0 && c >= 0 && r < n && c < n && maze[r, c] == 1;
    }
 
    // Function to get all valid paths
    static void FindPath(int r, int c, int[,] maze, int n, List<string> ans, ref string currentPath)
    {
        // If we reach the bottom right cell of the matrix, add the current path to ans and return
        if (r == n - 1 && c == n - 1)
        {
            ans.Add(currentPath);
            return;
        }
 
        // Mark the current cell as blocked
        maze[r, c] = 0;
 
        for (int i = 0; i < 4; i++)
        {
            // Find the next row based on the current row (r) and the dr[] array
            int nextR = r + dr[i];
            // Find the next column based on the current column (c) and the dc[] array
            int nextC = c + dc[i];
 
            // Check if the next cell is valid or not
            if (IsValid(nextR, nextC, n, maze))
            {
                currentPath += direction[i];
                // Recursively call the FindPath function for the next cell
                FindPath(nextR, nextC, maze, n, ans, ref currentPath);
                currentPath = currentPath.Remove(currentPath.Length - 1); // Remove the last direction when backtracking
            }
        }
 
        // Mark the current cell as unblocked
        maze[r, c] = 1;
    }
 
    static void Main()
    {
        int n = 4;
 
        int[,] maze = {
            {1, 0, 0, 0},
            {1, 1, 0, 1},
            {1, 1, 0, 0},
            {0, 1, 1, 1}
        };
 
        // List to store all the valid paths
        List<string> result = new List<string>();
 
        // Store current Path
        string currentPath = "";
 
        // Function call to get all valid paths
        FindPath(0, 0, maze, n, result, ref currentPath);
 
        if (result.Count == 0)
        {
            Console.WriteLine(-1);
        }
        else
        {
            foreach (var path in result)
            {
                Console.Write(path + " ");
            }
            Console.WriteLine();
        }
    }
}


Javascript




class MazeSolver {
    // Initialize a string direction which represents all
    // the directions.
    static direction = "DLRU";
    // Arrays to represent change in rows and columns
    static dr = [1, 0, 0, -1];
    static dc = [0, -1, 1, 0];
 
    // Function to check if cell(r, c) is inside the maze
    // and unblocked
    static isValid(r, c, n, maze) {
        return r >= 0 && c >= 0 && r < n && c < n && maze[r] === 1;
    }
 
    // Function to get all valid paths
    static findPath(r, c, maze, n, ans, currentPath) {
        // If we reach the bottom right cell of the matrix,
        // add the current path to ans and return
        if (r === n - 1 && c === n - 1) {
            ans.push(currentPath.toString());
            return;
        }
 
        // Mark the current cell as blocked
        maze[r] = 0;
 
        for (let i = 0; i < 4; i++) {
            // Find the next row based on the current row
            // (r) and the dr[] array
            let nextr = r + MazeSolver.dr[i];
            // Find the next column based on the current
            // column (c) and the dc[] array
            let nextc = c + MazeSolver.dc[i];
            // Check if the next cell is valid or not
            if (MazeSolver.isValid(nextr, nextc, n, maze)) {
                currentPath += MazeSolver.direction.charAt(i);
                // Recursively call the solve function for
                // the next cell
                MazeSolver.findPath(nextr, nextc, maze, n, ans, currentPath);
                currentPath = currentPath.slice(0, -1);
            }
        }
        // Mark the current cell as unblocked
        maze[r] = 1;
    }
 
    static main() {
        const n = 4;
 
        const maze = [
            [1, 0, 0, 0],
            [1, 1, 0, 1],
            [1, 1, 0, 0],
            [0, 1, 1, 1],
        ];
 
        // List to store all the valid paths
        const result = [];
 
        // Store current Path
        let currentPath = "";
 
        // Function call to get all valid paths
        MazeSolver.findPath(0, 0, maze, n, result, currentPath);
 
        if (result.length === 0) {
            console.log(-1);
        } else {
            for (const path of result) {
                console.log(path + " ");
            }
        }
        console.log();
    }
}
 
// Call the main function
MazeSolver.main();


Output

DDRDRR DRDDRR 




Time Complexity: O(3^(m*n)), because on every cell we have to try 3 different directions.
Auxiliary Space: O(m*n), Maximum Depth of the recursion tree(auxiliary space).


Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!

Last Updated : 06 Dec, 2023
Like Article
Save Article
Previous
Next
Similar Reads
Complete Tutorials