Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Solving the Binary Land Problem Using Breadth-First Search

Tech May 14 1

This problem can be effectively solved using a Breadth-First Search (BFS) algoritmh. The core idea is to explore possible states, where a state is defined by the positions of two characters and the number of steps taken to reach that state.

We can represent a state using a structure that stores the coordinates of both characters and the current step count:

struct CharacterState {
    int char1_row, char1_col;
    int char2_row, char2_col;
    int steps;
};

To avoid redundant computations, we use a 4-dimensional boolean array visited to keep track of states that have already been explored. The dimensions correspond to the rows and columns of both characters.

Before transitioning to a new state, it's crucial to validate its feasibility. This involves checking for several conditions:

  1. Obstacles: If either character lands on an 'X' (representing a spider web), the state is invalid.
  2. Collisions: If a character attempts to move into a wall ('#'), its movement is reverted to its previous position in thatt step. If both characters encounter walls, the state is marked as visited and considered invalid.
  3. Visited States: If the new state has already been visited, we do not process it further.

If a state passes these checks, it is marked as visited.

bool isValidState(int& r1, int& c1, int& r2, int& c2, const CharacterState& prevState, bool visited[50][50][50][50]) {
    // Check for spider webs
    if (grid[r1][c1] == 'X' || grid[r2][c2] == 'X') {
        visited[r1][c1][r2][c2] = true;
        return false;
    }

    // Handle wall collisions
    if (grid[r1][c1] == '#') {
        visited[r1][c1][r2][c2] = true;
        r1 = prevState.char1_row;
        c1 = prevState.char1_col;
    }
    if (grid[r2][c2] == '#') {
        visited[r1][c1][r2][c2] = true;
        r2 = prevState.char2_row;
        c2 = prevState.char2_col;
    }

    // Check if the state has been visited
    if (visited[r1][c1][r2][c2]) {
        return false;
    }

    visited[r1][c1][r2][c2] = true;
    return true;
}

We can optimize the movement logic by pre-defining the possible moves for each character in arrays. For instance, move1 and move2 can store the row and column changes for four directions. It's beneficial to align these movement arrays so that move1[i] and move2[i] represent a corresponding move for each character simultaneously.

// Predefined moves for two characters
int move1[4][2] = {{1, 0}, {0, -1}, {0, 1}, {-1, 0}};
int move2[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};

The BFS implementation starts by initializing the queue with the starting state and marking it as visited. The algorithm then iteratively dequeues a state, explores all four possible moves for both characters, and enqueues valid, unvisited successor states. The process terminates when both characters reach the target ('T') simultaneously. The number of steps taken to reach this goal state is the solution.

#include <iostream>
#include <vector>
#include <queue>
#include <string>

struct CharacterState {
    int char1_row, char1_col;
    int char2_row, char2_col;
    int steps;
};

char grid[50][50];
bool visited[50][50][50][50];

int move1[4][2] = {{1, 0}, {0, -1}, {0, 1}, {-1, 0}};
int move2[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};

// Function to check state validity (as described above)
bool isValidState(...) { /* ... implementation ... */ }

void bfs(int start_r1, int start_c1, int start_r2, int start_c2, int R, int C) {
    std::queue<CharacterState> q;
    q.push({start_r1, start_c1, start_r2, start_c2, 0});
    visited[start_r1][start_c1][start_r2][start_c2] = true;

    while (!q.empty()) {
        CharacterState current = q.front();
        q.pop();

        for (int i = 0; i < 4; ++i) {
            int next_r1 = current.char1_row + move1[i][0];
            int next_c1 = current.char1_col + move1[i][1];
            int next_r2 = current.char2_row + move2[i][0];
            int next_c2 = current.char2_col + move2[i][1];

            CharacterState prevState = current;

            if (isValidState(next_r1, next_c1, next_r2, next_c2, prevState, visited)) {
                if (grid[next_r1][next_c1] == 'T' && grid[next_r2][next_c2] == 'T') {
                    std::cout << current.steps + 1 << std::endl;
                    return;
                }
                q.push({next_r1, next_c1, next_r2, next_c2, current.steps + 1});
            }
        }
    }

    std::cout << "no" << std::endl;
}

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(0);

    int R, C;
    std::cin >> R >> C;

    int start_G_r, start_G_c, start_M_r, start_M_c;

    for (int i = 1; i <= R; ++i) {
        for (int j = 1; j <= C; ++j) {
            std::cin >> grid[i][j];
            if (grid[i][j] == 'G') {
                start_G_r = i; start_G_c = j;
            } else if (grid[i][j] == 'M') {
                start_M_r = i; start_M_c = j;
            }
        }
    }

    bfs(start_G_r, start_G_c, start_M_r, start_M_c, R, C);

    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.