Submission #1188236

#TimeUsernameProblemLanguageResultExecution timeMemory
1188236ainunnajibRobots (APIO13_robots)C++20
30 / 100
1596 ms106296 KiB
#include <iostream>
#include <vector>
#include <string>
#include <queue>
// #include <set> // No longer needed
#include <unordered_set> // Use unordered_set for visited states
#include <map>
#include <tuple>   // For std::tie used in comparison
#include <vector>
#include <algorithm> // For std::sort
#include <cctype>    // For std::isdigit
#include <functional> // For std::hash

using namespace std;

// Struct to represent a robot (original or composite)
struct Robot {
    int min_l, max_l; // Min and max labels of merged robots
    int r, c;         // Row and column position

    // Comparison operator for sorting (needed for canonical state representation)
    bool operator<(const Robot& other) const {
        return tie(min_l, max_l, r, c) < tie(other.min_l, other.max_l, other.r, other.c);
    }
    // Equality operator (needed for unordered_set)
    bool operator==(const Robot& other) const {
        return tie(min_l, max_l, r, c) == tie(other.min_l, other.max_l, other.r, other.c);
    }
};

// --- Hashing Implementation ---

// Helper function to combine hashes (based on boost::hash_combine)
template <class T>
inline void hash_combine(size_t& seed, const T& v) {
    hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

// Hash function for Robot struct
struct RobotHash {
    size_t operator()(const Robot& r) const {
        size_t seed = 0;
        hash_combine(seed, r.min_l);
        hash_combine(seed, r.max_l);
        hash_combine(seed, r.r);
        hash_combine(seed, r.c);
        return seed;
    }
};

// Hash function for vector<Robot>
// IMPORTANT: Assumes the vector<Robot> is already sorted into canonical form!
struct VectorRobotHash {
     size_t operator()(const vector<Robot>& robots) const {
         size_t seed = robots.size(); // Start seed with size
         RobotHash robot_hasher;
         for(const auto& r : robots) {
             // Combine hash of each robot into the seed
             seed ^= robot_hasher(r) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
         }
         return seed;
     }
};


// --- Constants and Global Variables ---
const int MAX_H = 501;
const int MAX_W = 501;

int N;
int W;
int H;
vector<string> grid;
vector<pair<int, int>> initial_pos;

int dr[] = {-1, 0, 1, 0};
int dc[] = {0, 1, 0, -1};

pair<int, int> precomputed_moves[MAX_H][MAX_W][4];

// Function to check if a cell is valid
bool isValid(int r, int c) {
    return r >= 0 && r < H && c >= 0 && c < W && grid[r][c] != 'x';
}

// Precomputation Function (No changes needed here)
void precompute_all_moves() {
    for (int r = 0; r < H; ++r) {
        for (int c = 0; c < W; ++c) {
            if (grid[r][c] == 'x') {
                 for (int start_dir = 0; start_dir < 4; ++start_dir) precomputed_moves[r][c][start_dir] = {-1, -1};
                 continue;
            }
            for (int start_dir = 0; start_dir < 4; ++start_dir) {
                int nr = r, nc = c;
                int current_dir = start_dir;
                bool starting_on_rotator = (grid[r][c] == 'A' || grid[r][c] == 'C');
                int first_step_dir = start_dir;
                if (starting_on_rotator) {
                    int rotated_dir = (grid[r][c] == 'A') ? (start_dir + 3) % 4 : (start_dir + 1) % 4;
                    int rotated_next_r = r + dr[rotated_dir];
                    int rotated_next_c = c + dc[rotated_dir];
                    if (isValid(rotated_next_r, rotated_next_c)) {
                        first_step_dir = rotated_dir;
                    }
                }
                int first_step_r = r + dr[first_step_dir];
                int first_step_c = c + dc[first_step_dir];
                if (!isValid(first_step_r, first_step_c)) {
                    precomputed_moves[r][c][start_dir] = {r, c};
                    continue;
                }
                nr = first_step_r; nc = first_step_c;
                current_dir = first_step_dir;
                while (true) {
                    if (grid[nr][nc] == 'A') current_dir = (current_dir + 3) % 4;
                    else if (grid[nr][nc] == 'C') current_dir = (current_dir + 1) % 4;
                    int next_r = nr + dr[current_dir];
                    int next_c = nc + dc[current_dir];
                    if (!isValid(next_r, next_c)) break;
                    nr = next_r; nc = next_c;
                }
                precomputed_moves[r][c][start_dir] = {nr, nc};
            }
        }
    }
}


// Optimized simulate_move using precomputation
pair<int, int> simulate_move(int r, int c, int dir) {
     return precomputed_moves[r][c][dir];
}

// Function to perform merges (No changes needed here)
// IMPORTANT: This function sorts the vector at the end, which is required for hashing.
void perform_merges(vector<Robot>& current_robots) {
     if (current_robots.size() < 2) return;
     bool merged_in_pass = true;
     while (merged_in_pass) {
         merged_in_pass = false;
         vector<Robot> next_round_robots;
         vector<bool> processed(current_robots.size(), false);
         // Sort by position first to efficiently find robots at the same location
         sort(current_robots.begin(), current_robots.end(), [](const Robot& a, const Robot& b){
              return tie(a.r, a.c, a.min_l, a.max_l) < tie(b.r, b.c, b.min_l, b.max_l);
              });
         for (int i = 0; i < current_robots.size(); ++i) {
             if (processed[i]) continue;
             bool merged_robot_i = false;
             // Check subsequent robots ONLY if they are at the same location
             for (int j = i + 1; j < current_robots.size(); ++j) {
                  if (processed[j]) continue;
                  // Optimization: If position differs, no more robots at location i can be found
                  if (current_robots[i].r != current_robots[j].r || current_robots[i].c != current_robots[j].c) break;

                  // Check compatibility
                  if (current_robots[i].max_l + 1 == current_robots[j].min_l || current_robots[j].max_l + 1 == current_robots[i].min_l) {
                      Robot merged_robot;
                      merged_robot.min_l = min(current_robots[i].min_l, current_robots[j].min_l);
                      merged_robot.max_l = max(current_robots[i].max_l, current_robots[j].max_l);
                      merged_robot.r = current_robots[i].r;
                      merged_robot.c = current_robots[i].c;
                      next_round_robots.push_back(merged_robot);
                      processed[i] = true; processed[j] = true;
                      merged_in_pass = true; merged_robot_i = true;
                      break; // Robot i merged, move to next i
                  }
             }
             // If robot i wasn't processed (didn't merge), add it back
             if (!processed[i]) {
                 next_round_robots.push_back(current_robots[i]);
                 processed[i] = true; // Mark as processed (kept)
             }
         }
         current_robots = next_round_robots; // Update for next pass
          if (current_robots.size() < 2) break; // Stop if few robots left
     }
     // Final sort by labels/pos for canonical representation before hashing/storing
     sort(current_robots.begin(), current_robots.end());
 }


// Solves the problem using BFS with precomputed moves and hashing
int solve() {
    vector<Robot> initial_state;
    for (int i = 0; i < N; ++i) {
        initial_state.push_back({i + 1, i + 1, initial_pos[i].first, initial_pos[i].second});
    }
    perform_merges(initial_state); // Sorts the state canonically

    queue<pair<vector<Robot>, int>> q;
    // *** Use unordered_set with custom hash function ***
    unordered_set<vector<Robot>, VectorRobotHash> visited;

    q.push({initial_state, 0});
    visited.insert(initial_state); // Insert requires state to be sorted

    while (!q.empty()) {
        vector<Robot> current_robots = q.front().first; // Copy state
        int current_pushes = q.front().second;
        q.pop();

        // Goal check
        if (current_robots.size() == 1 && current_robots[0].min_l == 1 && current_robots[0].max_l == N) {
            return current_pushes;
        }

        // Generate next states
        for (int i = 0; i < current_robots.size(); ++i) {
            for (int dir_idx = 0; dir_idx < 4; ++dir_idx) {
                // It's often faster to modify a copy than to create a new vector each time
                vector<Robot> next_robots = current_robots;
                Robot moving_robot = next_robots[i]; // Get robot to move (before potential modification)

                pair<int, int> final_pos = simulate_move(moving_robot.r, moving_robot.c, dir_idx);

                if (final_pos.first == -1) continue; // Invalid start pos for move

                // Update the specific robot that moved
                next_robots[i].r = final_pos.first;
                next_robots[i].c = final_pos.second;

                // Perform merges. This sorts the vector `next_robots` canonically.
                perform_merges(next_robots);

                // Check visited using unordered_set's find (average O(1) + hashing cost)
                if (visited.find(next_robots) == visited.end()) {
                    visited.insert(next_robots); // Insert requires state to be sorted
                    q.push({next_robots, current_pushes + 1});
                }
            }
        }
    }
    return -1; // Goal not reachable
}

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

    cin >> N >> W >> H;

    grid.resize(H);
    initial_pos.resize(N);
    vector<bool> pos_found(N, false);

    for (int i = 0; i < H; ++i) {
        cin >> grid[i];
        for (int j = 0; j < W; ++j) {
            if (isdigit(grid[i][j])) {
                int robot_label = grid[i][j] - '0';
                int robot_id = robot_label - 1;
                if (robot_id >= 0 && robot_id < N) {
                   initial_pos[robot_id] = {i, j};
                   pos_found[robot_id] = true;
                   grid[i][j] = '.';
                } else {
                     grid[i][j] = '.';
                }
            }
        }
    }

    bool all_found = true;
    for(int i=0; i<N; ++i) {
        if (!pos_found[i]) {
            all_found = false;
            cerr << "Error: Robot " << (i+1) << " not found on the initial grid." << endl;
             return 1;
        }
    }

    precompute_all_moves();
    cout << solve() << endl;

    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...