Open In App

CSES Solutions – Grid Paths

There are 88418 paths in a 7x7 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:

Below is the implementation of the approach:

// 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;
}
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
# 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))
// 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)

Article Tags :