Open In App

CSES Solutions – Grid Paths

Last Updated : 11 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

There are 88418 paths in a 7×7 grid from the upper-left square to the lower-left square. Each path corresponds to a 48-character description consisting of characters D (down), U (up), L (left) and R (right). You are given a description of a path which may also contain characters ? (any direction). Your task is to calculate the number of paths that match the description.

Example

Input: ??????R??????U??????????????????????????LD????D?

Output: 201

Explanation: There are 201 possible paths with the given description.

Approach using DFS:

The idea is to use DFS and try all four directions if the current character in the string is ‘?’ else we go in direction given (L,R,U,D).
Pruning/Terminating the search in order to optimize the code:
If we cannot go up or down but can turn either left or right or if we cannot turn left or right but can go up and down, in this case the grid splits into two parts. It is clear that we cannot visit one part of the grid, so we can terminate the search. These four cases also leaves unvisited squares which can cannot be visited. Hence, the search needs to terminate.

  • If the upper-right diagonal square is visited and the up and right squares are unvisited.
  • If the lower-right diagonal square is visited and the down and right squares are unvisited.
  • If the upper-left diagonal square is visited and the up and left squares are unvisited.
  • If the lower-left diagonal square is visited and the down and right squares are unvisited.

Step-by-step algorithm:

  • Define a function countPaths that takes the current position (x, y) and the current position in the path description string pos as parameters.
  • Base Case Return 1:
    • if all characters in the string have been processed and if the current position is the lower-left square else 0.
  • Base Case Return 0:
    • If the current cell has already been visited.
    • If the current position is the lower-left square before processing all characters.
    • If the current position is such that the up and down squares are unvisited and the left and right squares are visited.
    • If the current position is such that the left and right squares are unvisited and the up and down squares are visited.
    • If the current position is such that the upper-right diagonal square is visited and the up and right squares are unvisited.
    • If the current position is such that the lower-right diagonal square is visited and the down and right squares are unvisited.
    • If the current position is such that the upper-left diagonal square is visited and the up and left squares are unvisited.
    • If the current position is such that the lower-left diagonal square is visited and the down and left squares are unvisited.
  • Mark the current cell as visited.
  • Initialize a variable to store the number of paths.
  • Check if the current character in the string is ‘?’. If true, try all four directions.
  • Check if the current character in the string is a direction (L,R,U,D). If true, go in that direction.
  • Unmark the current cell.
  • Return the number of paths.

Below is the implementation of the approach:

C++
// C++ Code
#include <bits/stdc++.h>
using namespace std;

// Macro to check if a coordinate is valid in the grid
#define isValid(a) (a >= 0 && a < 7 ? 1 : 0)

// Direction constants
#define right 0
#define left 1
#define down 2
#define up 3

// Direction vectors for right, left, down, and up
int dx[4] = { 0, 0, 1, -1 };
int dy[4] = { 1, -1, 0, 0 };

// The path description string
string str;
int vis[7][7];

// Function to count the number of paths that match the
// description
int countPaths(int x, int y, int pos)
{
    // If we have processed all characters in the string and
    // we are at the lower-left square, return 1
    if (pos == (int)str.length())
        return (x == 6 && y == 0);

    // If we have reached the lower-left square before
    // processing all characters, return 0
    if (x == 6 && y == 0)
        return 0;

    // If the current cell is already visited, return 0
    if (vis[x][y])
        return 0;

    // Array to keep track of the visited status of the
    // neighboring cells
    vector<bool> visited(4, -1);
    for (int k = 0; k < 4; k++)
        if (isValid(x + dx[k]) && isValid(y + dy[k]))
            visited[k] = vis[x + dx[k]][y + dy[k]];

    // If we are at a position such that the up and down
    // squares are unvisited and the left and right squares
    // are visited return 0
    if (!visited[down] && !visited[up] && visited[right]
        && visited[left])
        return 0;

    // If we are at a position such that the left and right
    // squares are unvisited and the up and down squares are
    // visited return 0
    if (!visited[right] && !visited[left] && visited[down]
        && visited[up])
        return 0;

    // If we are at a position such that the upper-right
    // diagonal square is visited and the up and right
    // squares are unvisited return 0
    if (isValid(x - 1) && isValid(y + 1)
        && vis[x - 1][y + 1] == 1)
        if (!visited[right] && !visited[up])
            return 0;

    // If we are at a position such that the lower-right
    // diagonal square is visited and the down and right
    // squares are unvisited return 0
    if (isValid(x + 1) && isValid(y + 1)
        && vis[x + 1][y + 1] == 1)
        if (!visited[right] && !visited[down])
            return 0;

    // If we are at a position such that the upper-left
    // diagonal square is visited and the up and left
    // squares are unvisited return 0
    if (isValid(x - 1) && isValid(y - 1)
        && vis[x - 1][y - 1] == 1)
        if (!visited[left] && !visited[up])
            return 0;

    // If we are at a position such that the lower-left diagonal
    // square is visited and the down and right squares are
    // unvisited return 0
    if (isValid(x + 1) && isValid(y - 1)
        && vis[x + 1][y - 1] == 1)
        if (!visited[left] && !visited[down])
            return 0;

    // Mark the current cell as visited
    vis[x][y] = 1;

    // Variable to store the number of paths
    int numberOfPaths = 0;

    // If the current character is '?', try all four
    // directions
    if (str[pos] == '?') {
        for (int k = 0; k < 4; k++)
            if (isValid(x + dx[k]) && isValid(y + dy[k]))
                numberOfPaths += countPaths(
                    x + dx[k], y + dy[k], pos + 1);
    }

    // If the current character is a direction, go in that
    // direction
    else if (str[pos] == 'R' && y + 1 < 7)
        numberOfPaths += countPaths(x, y + 1, pos + 1);
    else if (str[pos] == 'L' && y - 1 >= 0)
        numberOfPaths += countPaths(x, y - 1, pos + 1);
    else if (str[pos] == 'U' && x - 1 >= 0)
        numberOfPaths += countPaths(x - 1, y, pos + 1);
    else if (str[pos] == 'D' && x + 1 < 7)
        numberOfPaths += countPaths(x + 1, y, pos + 1);

    // Unmark the current cell
    vis[x][y] = 0;

    // Return the number of paths
    return numberOfPaths;
}

// Driver Code
int main()
{
    // Example 1:
    str = "??????R??????U??????????????????????????LD????"
          "D?";
    cout << countPaths(0, 0, 0) << endl;
}
Java
public class Main {
    // Constants
    private static final int DIR_LEN = 4;
    private static final int[] dr = {-1, 0, 1, 0};
    private static final int[] dc = {0, 1, 0, -1};
    private static final int PATH_LEN = 48; // Length of all possible paths
    private static final int GRID_SIZE = 9;

    // Variables
    private static int[] p = new int[PATH_LEN];
    private static boolean[][] onPath = new boolean[GRID_SIZE][GRID_SIZE];

    public static int tryPath(int pathIdx, int curR, int curC) {
        // Optimization 3
        if ((onPath[curR][curC - 1] && onPath[curR][curC + 1]) &&
                (!onPath[curR - 1][curC] && !onPath[curR + 1][curC]))
            return 0;
        if ((onPath[curR - 1][curC] && onPath[curR + 1][curC]) &&
                (!onPath[curR][curC - 1] && !onPath[curR][curC + 1]))
            return 0;

        if (curR == 7 && curC == 1) { // Reached endpoint before visiting all
            if (pathIdx == PATH_LEN) return 1;
            return 0;
        }

        if (pathIdx == PATH_LEN) return 0;

        int ret = 0;
        onPath[curR][curC] = true;

        // Turn already determined:
        if (p[pathIdx] < 4) {
            int nxtR = curR + dr[p[pathIdx]];
            int nxtC = curC + dc[p[pathIdx]];
            if (!onPath[nxtR][nxtC]) ret += tryPath(pathIdx + 1, nxtR, nxtC);
        } else { // Iterate through all four possible turns
            for (int i = 0; i < DIR_LEN; i++) {
                int nxtR = curR + dr[i];
                int nxtC = curC + dc[i];
                if (onPath[nxtR][nxtC]) continue;
                ret += tryPath(pathIdx + 1, nxtR, nxtC);
            }
        }

        // Reset and return
        onPath[curR][curC] = false;
        return ret;
    }

    public static void main(String[] args) {
        String line = "??????R??????U??????????????????????????LD????D?";

        // Convert path to ints
        for (int i = 0; i < PATH_LEN; i++) {
            char cur = line.charAt(i);
            if (cur == 'U') p[i] = 0;
            else if (cur == 'R') p[i] = 1;
            else if (cur == 'D') p[i] = 2;
            else if (cur == 'L') p[i] = 3;
            else p[i] = 4; // cur == '?'
        }

        // Set borders of grid
        for (int i = 0; i < GRID_SIZE; i++) {
            onPath[0][i] = true;
            onPath[8][i] = true;
            onPath[i][0] = true;
            onPath[i][8] = true;
        }

        // Initialize the inside of the grid to be completely empty
        for (int i = 1; i <= 7; i++) {
            for (int j = 1; j <= 7; j++) {
                onPath[i][j] = false;
            }
        }

        int startIdx = 0;
        int startR = 1;
        int startC = 1; // Always start path at (1, 1)
        int ans = tryPath(startIdx, startR, startC);
        System.out.println(ans);
    }
}
//This code is contrbiuted by Utkarsh
Python3
# Import the necessary libraries
import numpy as np

# Direction vectors for right, left, down, and up
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]

# The path description string
str_path = "??????R??????U??????????????????????????LD????D?"

# Initialize the visited array
vis = np.zeros((7, 7))

# Function to check if a coordinate is valid in the grid
def isValid(a):
    return 1 if a >= 0 and a < 7 else 0

# Function to count the number of paths that match the description
def countPaths(x, y, pos):
    # If we have processed all characters in the string and we are at the lower-left square, return 1
    if pos == len(str_path):
        return 1 if x == 6 and y == 0 else 0

    # If we have reached the lower-left square before processing all characters, return 0
    if x == 6 and y == 0:
        return 0

    # If the current cell is already visited, return 0
    if vis[x][y]:
        return 0

    # Array to keep track of the visited status of the neighboring cells
    visited = [-1]*4
    for k in range(4):
        if isValid(x + dx[k]) and isValid(y + dy[k]):
            visited[k] = vis[x + dx[k]][y + dy[k]]

    # If we are at a position such that the up and down squares are unvisited and the left and right squares are visited return 0
    if not visited[2] and not visited[3] and visited[0] and visited[1]:
        return 0

    # If we are at a position such that the left and right squares are unvisited and the up and down squares are visited return 0
    if not visited[0] and not visited[1] and visited[2] and visited[3]:
        return 0

    # If we are at a position such that the upper-right diagonal square is visited and the up and right squares are unvisited return 0
    if isValid(x - 1) and isValid(y + 1) and vis[x - 1][y + 1] == 1:
        if not visited[0] and not visited[3]:
            return 0

    # If we are at a position such that the lower-right diagonal square is visited and the down and right squares are unvisited return 0
    if isValid(x + 1) and isValid(y + 1) and vis[x + 1][y + 1] == 1:
        if not visited[0] and not visited[2]:
            return 0

    # If we are at a position such that the upper-left diagonal square is visited and the up and left squares are unvisited return 0
    if isValid(x - 1) and isValid(y - 1) and vis[x - 1][y - 1] == 1:
        if not visited[1] and not visited[3]:
            return 0

    # If we are at a position such that the lower-left diagonal square is visited and the down and right squares are unvisited return 0
    if isValid(x + 1) and isValid(y - 1) and vis[x + 1][y - 1] == 1:
        if not visited[1] and not visited[2]:
            return 0

    # Mark the current cell as visited
    vis[x][y] = 1

    # Variable to store the number of paths
    numberOfPaths = 0

    # If the current character is '?', try all four directions
    if str_path[pos] == '?':
        for k in range(4):
            if isValid(x + dx[k]) and isValid(y + dy[k]):
                numberOfPaths += countPaths(x + dx[k], y + dy[k], pos + 1)

    # If the current character is a direction, go in that direction
    elif str_path[pos] == 'R' and y + 1 < 7:
        numberOfPaths += countPaths(x, y + 1, pos + 1)
    elif str_path[pos] == 'L' and y - 1 >= 0:
        numberOfPaths += countPaths(x, y - 1, pos + 1)
    elif str_path[pos] == 'U' and x - 1 >= 0:
        numberOfPaths += countPaths(x - 1, y, pos + 1)
    elif str_path[pos] == 'D' and x + 1 < 7:
        numberOfPaths += countPaths(x + 1, y, pos + 1)

    # Unmark the current cell
    vis[x][y] = 0

    # Return the number of paths
    return numberOfPaths

# Call the function and print the result
print(countPaths(0, 0, 0))
JavaScript
// Macro to check if a coordinate is valid in the grid
const isValid = (a) => (a >= 0 && a < 7 ? 1 : 0);

// Direction constants
const right = 0;
const left = 1;
const down = 2;
const up = 3;

// Direction vectors for right, left, down, and up
const dx = [0, 0, 1, -1];
const dy = [1, -1, 0, 0];

// The path description string
let str;
const vis = new Array(7).fill(null).map(() => new Array(7).fill(0));

// Function to count the number of paths that match the
// description
function countPaths(x, y, pos) {
    // If we have processed all characters in the string and
    // we are at the lower-left square, return 1
    if (pos === str.length)
        return (x === 6 && y === 0) ? 1 : 0;

    // If we have reached the lower-left square before
    // processing all characters, return 0
    if (x === 6 && y === 0)
        return 0;

    // If the current cell is already visited, return 0
    if (vis[x][y])
        return 0;

    // Array to keep track of the visited status of the
    // neighboring cells
    const visited = new Array(4).fill(-1);
    for (let k = 0; k < 4; k++)
        if (isValid(x + dx[k]) && isValid(y + dy[k]))
            visited[k] = vis[x + dx[k]][y + dy[k]];

    // If we are at a position such that the up and down
    // squares are unvisited and the left and right squares
    // are visited return 0
    if (!visited[down] && !visited[up] && visited[right]
        && visited[left])
        return 0;

    // If we are at a position such that the left and right
    // squares are unvisited and the up and down squares are
    // visited return 0
    if (!visited[right] && !visited[left] && visited[down]
        && visited[up])
        return 0;

    // If we are at a position such that the upper-right
    // diagonal square is visited and the up and right
    // squares are unvisited return 0
    if (isValid(x - 1) && isValid(y + 1)
        && vis[x - 1][y + 1] === 1)
        if (!visited[right] && !visited[up])
            return 0;

    // If we are at a position such that the lower-right
    // diagonal square is visited and the down and right
    // squares are unvisited return 0
    if (isValid(x + 1) && isValid(y + 1)
        && vis[x + 1][y + 1] === 1)
        if (!visited[right] && !visited[down])
            return 0;

    // If we are at a position such that the upper-left
    // diagonal square is visited and the up and left
    // squares are unvisited return 0
    if (isValid(x - 1) && isValid(y - 1)
        && vis[x - 1][y - 1] === 1)
        if (!visited[left] && !visited[up])
            return 0;

    // If we are at a position such that the lower-left diagonal
    // square is visited and the down and right squares are
    // unvisited return 0
    if (isValid(x + 1) && isValid(y - 1)
        && vis[x + 1][y - 1] === 1)
        if (!visited[left] && !visited[down])
            return 0;

    // Mark the current cell as visited
    vis[x][y] = 1;

    // Variable to store the number of paths
    let numberOfPaths = 0;

    // If the current character is '?', try all four
    // directions
    if (str[pos] === '?') {
        for (let k = 0; k < 4; k++)
            if (isValid(x + dx[k]) && isValid(y + dy[k]))
                numberOfPaths += countPaths(
                    x + dx[k], y + dy[k], pos + 1);
    }

    // If the current character is a direction, go in that
    // direction
    else if (str[pos] === 'R' && y + 1 < 7)
        numberOfPaths += countPaths(x, y + 1, pos + 1);
    else if (str[pos] === 'L' && y - 1 >= 0)
        numberOfPaths += countPaths(x, y - 1, pos + 1);
    else if (str[pos] === 'U' && x - 1 >= 0)
        numberOfPaths += countPaths(x - 1, y, pos + 1);
    else if (str[pos] === 'D' && x + 1 < 7)
        numberOfPaths += countPaths(x + 1, y, pos + 1);

    // Unmark the current cell
    vis[x][y] = 0;

    // Return the number of paths
    return numberOfPaths;
}

// Driver Code
(function main() {
    // Example 1:
    str = "??????R??????U??????????????????????????LD????D?";
    console.log(countPaths(0, 0, 0));
})();

Output
201

Time Complexity: O(4N), where N is the length of string.
Auxiliary Space: O(N)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads