#include #include #include #include #include #include #include #include // #include "graph.h" using namespace std; using namespace chrono; int I, N = 0; struct Node { vector board; string change; int zero; int currentDepth; int remainingDepth; Node() : board(), change(), zero(), currentDepth(), remainingDepth() {} void update_zero() { for (this->zero = 0; this->zero < this->board.size(); this->zero++) { if (this->board[this->zero] == 0) return; } } void update_remainingDepth() { this->remainingDepth = this->manhattan_dist(); } bool solved() { return this->remainingDepth == 0; } int depth() { return this->currentDepth + this->remainingDepth; } bool equals_any_in(vector& arr) { for (int i = 0; i < arr.size(); i++) { if (*arr[i] == *this) { return true; } } return false; } void increment(int new_zero) { swap(this->board[new_zero], this->board[this->zero]); this->zero = new_zero; this->currentDepth++; this->update_remainingDepth(); } private: pair to_2D(int i) { return { i / N, i % N }; } int manhattan_dist() { int total = 0; for (int i = 0; i < this->board.size(); i++) { if (this->board[i] == 0) continue; auto curr = to_2D(i); auto goal = to_2D(this->board[i] - 1 + (this->board[i] > I)); total += abs(curr.first - goal.first) + abs(curr.second - goal.second); } return total; } public: Node& operator=(const Node& other) { if (this != &other) { this->change = other.change; this->board = other.board; this->zero = other.zero; this->currentDepth = other.currentDepth; this->remainingDepth = other.remainingDepth; } return *this; } friend bool operator==(const Node& left, const Node& right) { return left.board == right.board; } }; #define SOLVED -1 #define SKIP_J_IF(condition) if (condition) { delete j; continue; } int dfs(vector& path, int maxDepth) { Node* i = path.back(); if (i->solved()) return SOLVED; if (i->depth() > maxDepth) return i->depth(); int min = INT_MAX; for (int dir = 0; dir < 4; dir++) { Node* j = new Node(*i); int new_zero = 0; switch (dir) { case 0: // left SKIP_J_IF(j->zero % N == 0); new_zero = j->zero - 1; j->change = "right"; break; case 1: // right SKIP_J_IF(j->zero % N == N - 1); new_zero = j->zero + 1; j->change = "left"; break; case 2: // up SKIP_J_IF(j->zero / N == 0); new_zero = j->zero - N; j->change = "down"; break; case 3: // down SKIP_J_IF(j->zero / N == N - 1); new_zero = j->zero + N; j->change = "up"; break; } j->increment(new_zero); // Don't go back a step SKIP_J_IF(j->equals_any_in(path)); path.push_back(j); int val = dfs(path, maxDepth); if (val == SOLVED) return SOLVED; if (val < min) min = val; path.pop_back(); delete j; } return min; } bool solvable(Node* root) { vector &board = root->board; int inversions = 0; for (int i = 0; i < board.size(); i++) { if (board[i] == 0) continue; for (int j = i; j < board.size(); j++) { if (board[j] == 0) continue; inversions += board[i] > board[j]; } } // n is even, an n-by-n board is solvable // iff the number of inversions + the row of the blank square is odd if (board.size() % 2 == 0) { inversions += root->zero / N; return (inversions % 2) == ((I / N) % 2); } // when n is odd, an n-by-n board is solvable // iff its number of inversions is even else { return inversions % 2 == 0; } } int main(int argc, char* argv[]) { /* Input */ cin >> N >> I; if (I == -1) { I = N; } N++; Node* root = new Node; int temp; for (int i = 0; i < N; i++) { cin >> temp; root->board.push_back(temp); } N = sqrt(N + 1); /* Prepare */ root->update_zero(); if (!solvable(root)) { std::cout << -1 << std::endl; delete root; return 0; } root->update_remainingDepth(); vector path; path.push_back(root); /* IDA* */ auto startTime = system_clock::now(); int bound = 3; while(bound != SOLVED) { bound = dfs(path, bound); } auto endTime = system_clock::now(); /* Output */ cout << (path.size() - 1) << endl; for (int i = 1; i < path.size(); i++) { cout << path[i]->change << endl; delete path[i]; } delete root; if (argc > 1) { auto millis = duration_cast(endTime - startTime).count(); std::cout << "Took: " << millis / 1000.0 << "s" << std::endl; } }