結果

問題 No.1323 うしらずSwap
ユーザー qwewe
提出日時 2025-05-14 13:17:12
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
TLE  
実行時間 -
コード長 14,030 bytes
コンパイル時間 1,139 ms
コンパイル使用メモリ 106,540 KB
実行使用メモリ 161,700 KB
最終ジャッジ日時 2025-05-14 13:18:41
合計ジャッジ時間 6,793 ms
ジャッジサーバーID
(参考情報)
judge5 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 13 TLE * 1 -- * 45
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <map> // Using map for storing distances. Keys are state representations.
#include <utility> // For pair
#include <algorithm> // For min

using namespace std;

// Type definitions for convenience
typedef long long ll; // Use long long for state representation key
typedef pair<int, int> pii; // Represents coordinates (row, col)

// Global variables
int H, W;             // Grid dimensions
vector<string> S;     // Grid layout storing '#' for obstacles and '.' for empty cells
ll N;                 // Total number of cells H * W, used for state encoding

// Directions for movement: up, down, left, right
int dr[] = {-1, 1, 0, 0};
int dc[] = {0, 0, -1, 1};

// Convert a state represented by two positions (posA, posB) to a unique long long key.
// This is done by mapping each position (r, c) to a single index and then combining the two indices.
ll state_to_ll(pii posA, pii posB) {
    // Convert 0-based (r, c) coordinates to a single index: row * W + col
    ll idxA = (ll)posA.first * W + posA.second;
    ll idxB = (ll)posB.first * W + posB.second;
    // Combine two indices into a single key. N = H*W. Key = idxA * N + idxB.
    // This ensures a unique key for each state ((rA, cA), (rB, cB)).
    return idxA * N + idxB;
}

// Convert a long long key back to a state (pair of positions).
// This is the inverse operation of state_to_ll.
pair<pii, pii> ll_to_state(ll key) {
    ll idxB = key % N; // Extract index of B
    ll idxA = key / N; // Extract index of A
    // Convert single indices back to (r, c) coordinates
    pii posA = {(int)(idxA / W), (int)(idxA % W)}; // (row = index / W, col = index % W)
    pii posB = {(int)(idxB / W), (int)(idxB % W)};
    return {posA, posB};
}

// Check if coordinates (r, c) are within grid bounds and the cell is empty ('.').
// The problem guarantees the grid is surrounded by '#', so moving into boundary cells is naturally prevented.
// However, explicitly checking bounds is safer.
bool is_valid(int r, int c) {
    return r >= 0 && r < H && c >= 0 && c < W && S[r][c] == '.';
}

int main() {
    // Faster I/O operations
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Read input: grid dimensions H, W and initial positions (1-based)
    int ra_start, ca_start, rb_start, cb_start;
    cin >> H >> W >> ra_start >> ca_start >> rb_start >> cb_start;
    N = (ll)H * W; // Calculate total number of cells

    // Read grid layout
    S.resize(H);
    for (int i = 0; i < H; ++i) {
        cin >> S[i];
    }

    // Adjust coordinates to 0-based indexing for internal calculations
    pii start_A = {ra_start - 1, ca_start - 1};
    pii start_B = {rb_start - 1, cb_start - 1};
    // Target positions are the swapped initial positions
    pii target_A = {rb_start - 1, cb_start - 1}; 
    pii target_B = {ra_start - 1, ca_start - 1}; 

    // Encode initial and target states into unique long long keys
    ll initial_key = state_to_ll(start_A, start_B);
    ll target_key = state_to_ll(target_A, target_B);

    // If the initial state is already the target state, 0 moves are needed.
    if (initial_key == target_key) {
        cout << 0 << endl;
        return 0;
    }

    // Data structures for Bidirectional BFS
    map<ll, int> dist_f; // Stores distances from the initial state (forward search)
    map<ll, int> dist_b; // Stores distances from the target state (backward search)
    queue<ll> q_f;       // Queue for states in the forward search frontier
    queue<ll> q_b;       // Queue for states in the backward search frontier

    // Initialize forward search from the initial state
    dist_f[initial_key] = 0;
    q_f.push(initial_key);

    // Initialize backward search from the target state
    dist_b[target_key] = 0;
    q_b.push(target_key);

    int min_total_dist = -1; // Stores the minimum total distance found so far, initialized to -1 (unreachable)

    // Main loop for Bidirectional BFS. Continues as long as both search frontiers are non-empty.
    while (!q_f.empty() && !q_b.empty()) {
        
        // Optimization: Check if the sum of minimum distances from both frontiers is already greater than or equal to the best path found.
        // If so, no shorter path can be found, so we can terminate the search early.
        if (min_total_dist != -1) {
             // Get distance of states at the front of queues (these represent the minimum distances currently being explored)
             int current_min_dist_f = dist_f[q_f.front()];
             int current_min_dist_b = dist_b[q_b.front()];
             if (current_min_dist_f + current_min_dist_b >= min_total_dist) {
                 break; 
             }
        }

        // Strategy: Expand the smaller frontier to keep the search balanced. This heuristic helps optimize performance.
        if (q_f.size() <= q_b.size()) {
             // Expand forward queue (q_f) for one level (all nodes at the current distance)
             int current_level_size = q_f.size(); 
             for(int i = 0; i < current_level_size; ++i) {
                ll current_key = q_f.front(); // Get the current state key
                q_f.pop(); // Remove it from the queue

                pair<pii, pii> current_st = ll_to_state(current_key); // Decode key to positions
                pii posA = current_st.first;
                pii posB = current_st.second;
                int d = dist_f[current_key]; // Current distance from the start state
                
                // Check if this state has been reached by the backward search (meeting point found)
                if (dist_b.count(current_key)) {
                    int total_dist = d + dist_b[current_key]; // Calculate total path distance
                    // Update minimum total distance if this path is shorter
                    if (min_total_dist == -1 || total_dist < min_total_dist) {
                        min_total_dist = total_dist;
                    }
                }
                
                // Pruning optimization: If the current path length `d` is already too large compared to the best path found, 
                // skip exploring neighbours from this state as it cannot lead to a shorter path.
                 if(min_total_dist != -1 && d >= min_total_dist) continue; 

                // Explore neighbors by trying to move A
                for (int j = 0; j < 4; ++j) { // Iterate through 4 directions
                    int next_rA = posA.first + dr[j];
                    int next_cA = posA.second + dc[j];
                    pii next_posA = {next_rA, next_cA};

                    // Check validity of the move: within bounds, empty cell, and not occupied by B
                    if (is_valid(next_rA, next_cA) && next_posA != posB) {
                        ll next_key = state_to_ll(next_posA, posB); // Encode the new state
                        // If this neighbor state hasn't been visited yet in the forward search
                        if (!dist_f.count(next_key)) {
                            // Check distance limit before adding: if d+1 already reaches min_total_dist, no need to add.
                            if(min_total_dist != -1 && d + 1 >= min_total_dist) continue;

                            dist_f[next_key] = d + 1; // Record distance
                            q_f.push(next_key);       // Add to the forward queue
                            
                            // Immediately check if this new state meets the backward search frontier
                             if (dist_b.count(next_key)) {
                                 int total_dist = d + 1 + dist_b[next_key];
                                 if (min_total_dist == -1 || total_dist < min_total_dist) {
                                     min_total_dist = total_dist;
                                 }
                             }
                        }
                    }
                }

                // Explore neighbors by trying to move B
                 for (int j = 0; j < 4; ++j) { // Iterate through 4 directions
                    int next_rB = posB.first + dr[j];
                    int next_cB = posB.second + dc[j];
                    pii next_posB = {next_rB, next_cB};

                    // Check validity of the move: within bounds, empty cell, and not occupied by A
                    if (is_valid(next_rB, next_cB) && next_posB != posA) {
                        ll next_key = state_to_ll(posA, next_posB); // Encode the new state
                        // If this neighbor state hasn't been visited yet in the forward search
                        if (!dist_f.count(next_key)) {
                             // Check distance limit before adding
                            if(min_total_dist != -1 && d + 1 >= min_total_dist) continue;

                             dist_f[next_key] = d + 1; // Record distance
                             q_f.push(next_key);       // Add to the forward queue
                             
                             // Immediately check if this new state meets the backward search frontier
                             if (dist_b.count(next_key)) {
                                 int total_dist = d + 1 + dist_b[next_key];
                                 if (min_total_dist == -1 || total_dist < min_total_dist) {
                                     min_total_dist = total_dist;
                                 }
                             }
                        }
                    }
                 }
             }
        } else { // q_b size is smaller
             // Expand backward queue (q_b) for one level
             int current_level_size = q_b.size(); 
             for(int i = 0; i < current_level_size; ++i) {
                ll current_key = q_b.front(); // Get the current state key
                q_b.pop(); // Remove it from the queue

                pair<pii, pii> current_st = ll_to_state(current_key); // Decode key to positions
                pii posA = current_st.first;
                pii posB = current_st.second;
                int d = dist_b[current_key]; // Current distance from the target state

                // Check if this state has been reached by the forward search (meeting point found)
                if (dist_f.count(current_key)) {
                    int total_dist = d + dist_f[current_key]; // Calculate total path distance
                    // Update minimum total distance if this path is shorter
                    if (min_total_dist == -1 || total_dist < min_total_dist) {
                        min_total_dist = total_dist;
                    }
                 }
                 
                 // Pruning optimization similar to forward search
                 if(min_total_dist != -1 && d >= min_total_dist) continue;

                // Explore neighbors by trying to move A (moves are reversible, same logic as forward)
                for (int j = 0; j < 4; ++j) {
                    int next_rA = posA.first + dr[j];
                    int next_cA = posA.second + dc[j];
                    pii next_posA = {next_rA, next_cA};

                    if (is_valid(next_rA, next_cA) && next_posA != posB) {
                         ll next_key = state_to_ll(next_posA, posB); // Encode the new state
                        // If this neighbor state hasn't been visited yet in the backward search
                        if (!dist_b.count(next_key)) {
                            // Check distance limit before adding
                            if(min_total_dist != -1 && d + 1 >= min_total_dist) continue;

                             dist_b[next_key] = d + 1; // Record distance
                             q_b.push(next_key);       // Add to the backward queue
                             
                             // Immediately check if this new state meets the forward search frontier
                             if (dist_f.count(next_key)) {
                                 int total_dist = d + 1 + dist_f[next_key];
                                 if (min_total_dist == -1 || total_dist < min_total_dist) {
                                     min_total_dist = total_dist;
                                 }
                             }
                        }
                    }
                }

                // Explore neighbors by trying to move B
                 for (int j = 0; j < 4; ++j) {
                    int next_rB = posB.first + dr[j];
                    int next_cB = posB.second + dc[j];
                    pii next_posB = {next_rB, next_cB};

                    if (is_valid(next_rB, next_cB) && next_posB != posA) {
                        ll next_key = state_to_ll(posA, next_posB); // Encode the new state
                        // If this neighbor state hasn't been visited yet in the backward search
                        if (!dist_b.count(next_key)) {
                            // Check distance limit before adding
                            if(min_total_dist != -1 && d + 1 >= min_total_dist) continue;
                             
                             dist_b[next_key] = d + 1; // Record distance
                             q_b.push(next_key);       // Add to the backward queue
                             
                             // Immediately check if this new state meets the forward search frontier
                             if (dist_f.count(next_key)) {
                                 int total_dist = d + 1 + dist_f[next_key];
                                 if (min_total_dist == -1 || total_dist < min_total_dist) {
                                     min_total_dist = total_dist;
                                 }
                             }
                        }
                    }
                 }
             }
        }
    }

    // Output the minimum total distance found. If no path was found, min_total_dist remains -1.
    cout << min_total_dist << endl;

    return 0;
}
0