Ranking (as of 2013-03-23): 145
Language: C++
I used A* search algorithm and for its implementation, I consulted an article of Wikipedia (http://en.wikipedia.org/wiki/A*_search_algorithm).
Some inputs and the corresponding outputs:
3
6 2 8 4
12 14 1 10
13 15 3 9
11 0 5 7
UULDRURDDLLUURRDLURDRULULLDRDRRDLLLURRRULLURDDDR
6 8 4 2
12 14 1 10
13 15 3 9
11 0 5 7
RRULLDLURURRDDLURDLLURUULDLURRRDLLURRDLLLDRRRULDDR
10 1 7 12
5 8 11 2
9 4 3 6
13 14 15 0
LUURULDLDRRULLLURRDDDLLUURRDLLDRRRUUULDRDD
The priority queue implementation (the functions prefixed with "pqueue" and their associated definitions, etc. that are surrounded by the below 'extern "C"' block) is based on the code from https://github.com/vy/libpqueue and is licensed under the Apache 2.0 license:
  Copyright 2010 Volkan Yazici
  Copyright 2006-2010 The Apache Software Foundation
/* 8.6.2 15-Puzzle Problem PC/UVa IDs: 110802/10181, Popularity: B, Success rate: average Level: 3 To build using Visucal Studio 2008: cl -EHsc 15_puzzle_problem.ass.cpp */ #include <iostream> #include <sstream> #include <vector> #include <queue> #include <set> #include <map> #include <functional> #include <cstdlib> #ifdef DEBUG #include <cassert> #endif #ifdef __ELAPSED_TIME__ #include <ctime> #endif using namespace std; #ifdef __cplusplus extern "C" { #endif /** priority data type */ typedef int pqueue_pri_t; /** callback functions to get/set/compare the priority of an element */ typedef pqueue_pri_t (*pqueue_get_pri_f)(void *a); typedef void (*pqueue_set_pri_f)(void *a, pqueue_pri_t pri); typedef int (*pqueue_cmp_pri_f)(pqueue_pri_t next, pqueue_pri_t curr); /** callback functions to get/set the position of an element */ typedef size_t (*pqueue_get_pos_f)(void *a); typedef void (*pqueue_set_pos_f)(void *a, size_t pos); /** debug callback function to print a entry */ typedef void (*pqueue_print_entry_f)(FILE *out, void *a); /** the priority queue handle */ typedef struct pqueue_t { size_t size; size_t avail; size_t step; pqueue_cmp_pri_f cmppri; pqueue_get_pri_f getpri; pqueue_set_pri_f setpri; pqueue_get_pos_f getpos; pqueue_set_pos_f setpos; void **d; } pqueue_t; #define left(i) ((i) << 1) #define right(i) (((i) << 1) + 1) #define parent(i) ((i) >> 1) pqueue_t * pqueue_init(size_t n, pqueue_cmp_pri_f cmppri, pqueue_get_pri_f getpri, pqueue_set_pri_f setpri, pqueue_get_pos_f getpos, pqueue_set_pos_f setpos) { pqueue_t *q; if (!(q = (pqueue_t *)malloc(sizeof(pqueue_t)))) return NULL; /* Need to allocate n+1 elements since element 0 isn't used. */ if (!(q->d = (void **)(malloc((n + 1) * sizeof(void *))))) { free(q); return NULL; } q->size = 1; q->avail = q->step = (n+1); /* see comment above about n+1 */ q->cmppri = cmppri; q->setpri = setpri; q->getpri = getpri; q->getpos = getpos; q->setpos = setpos; return q; } void pqueue_free(pqueue_t *q) { free(q->d); free(q); } size_t pqueue_size(pqueue_t *q) { /* queue element 0 exists but doesn't count since it isn't used. */ return (q->size - 1); } static void bubble_up(pqueue_t *q, size_t i) { size_t parent_node; void *moving_node = q->d[i]; pqueue_pri_t moving_pri = q->getpri(moving_node); for (parent_node = parent(i); ((i > 1) && q->cmppri(q->getpri(q->d[parent_node]), moving_pri)); i = parent_node, parent_node = parent(i)) { q->d[i] = q->d[parent_node]; q->setpos(q->d[i], i); } q->d[i] = moving_node; q->setpos(moving_node, i); } static size_t maxchild(pqueue_t *q, size_t i) { size_t child_node = left(i); if (child_node >= q->size) return 0; if ((child_node+1) < q->size && q->cmppri(q->getpri(q->d[child_node]), q->getpri(q->d[child_node+1]))) child_node++; /* use right child instead of left */ return child_node; } static void percolate_down(pqueue_t *q, size_t i) { size_t child_node; void *moving_node = q->d[i]; pqueue_pri_t moving_pri = q->getpri(moving_node); while ((child_node = maxchild(q, i)) && q->cmppri(moving_pri, q->getpri(q->d[child_node]))) { q->d[i] = q->d[child_node]; q->setpos(q->d[i], i); i = child_node; } q->d[i] = moving_node; q->setpos(moving_node, i); } int pqueue_insert(pqueue_t *q, void *d) { void *tmp; size_t i; size_t newsize; if (!q) return 1; /* allocate more memory if necessary */ if (q->size >= q->avail) { newsize = q->size + q->step; if (!(tmp = realloc(q->d, sizeof(void *) * newsize))) return 1; q->d = (void **)tmp; q->avail = newsize; } /* insert item */ i = q->size++; q->d[i] = d; bubble_up(q, i); return 0; } void pqueue_change_priority(pqueue_t *q, pqueue_pri_t new_pri, void *d) { size_t posn; pqueue_pri_t old_pri = q->getpri(d); q->setpri(d, new_pri); posn = q->getpos(d); if (q->cmppri(old_pri, new_pri)) bubble_up(q, posn); else percolate_down(q, posn); } void * pqueue_pop(pqueue_t *q) { void *head; if (!q || q->size == 1) return NULL; head = q->d[1]; q->d[1] = q->d[--q->size]; percolate_down(q, 1); return head; } #ifdef __cplusplus } #endif /* unsigned long long state; // state of a puzzle Each state of a puzzle consists of the values stored in the 16 tiles. i-th tile (i >= 0) value is stored in the four consecutive bits located between bit (i * 4) and bit (i * 4 + 3). If the missing tile is at i-th tile, possible movements are: moving up is possible if i > 3. moving down is possible if i < 12. moving right is possible if (i % 4) < 3. movign left is possible if (i % 4) != 0. At each step, transfer to the at most four possible states the missing tile can move. Further Search is cancelled if it reaches either of the following condition: The puzzle gets solved. 50 steps has passed. */ const int nr_tiles = 16; // number of tiles const int g_score_max = 50; // number of max. steps const unsigned long long goal = 0x0fedcba987654321ULL; // the state of a puzzle when solved /* Solvability of the Tiles Game (http://www.cs.bham.ac.uk/~mdr/teaching/modules04/java2/TilesSolvability.html) */ bool is_puzzle_solvable(int tiles[]) { int ibr, nr_inversions = 0; for (int i = 0; i < nr_tiles; i++) { if (!tiles[i]) ibr = i / 4; else { for (int j = i + 1; j < nr_tiles; j++) if (tiles[j] && tiles[i] > tiles[j]) nr_inversions++; } } return (ibr & 1) ? !(nr_inversions & 1) : (nr_inversions & 1); } int heuristic_cost_estimate(unsigned long long state) { int heuristic_cost = 0; // for each tile, calcualate the Manhattan distance, i.e., // the distance between the current number of the tile and // the one that the tile should have for (int i = 0; i < nr_tiles; i++, state >>= 4) { int nr = static_cast<int>(state & 0xf) - 1; if (nr >= 0) // not the missing tile heuristic_cost += abs(nr / 4 - i / 4) + abs(nr % 4 - i % 4); /* else heuristic_cost += (3 - i / 4) + (3 - i % 4); */ } return 1.5 * heuristic_cost; } struct node { size_t pqueue_pos_; // used internally by libpqueue int mi_; // index to the missing tile unsigned long long state_; // state of tiles int g_score_; // Cost from start along best known path. int h_score_; // heuristic_cost_estimate(start, goal) int f_score_; // Estimated total cost from start to goal through y. node() : pqueue_pos_(-1), mi_(-1), state_(-1), g_score_(-1), h_score_(-1), f_score_(-1) {} node(int mi, unsigned long long state, int g_score) : pqueue_pos_(-1), mi_(mi), state_(state), g_score_(g_score), h_score_(heuristic_cost_estimate(state)), f_score_(g_score_ + h_score_) {} static int get_distance(void* vd); static void set_distance(void* vd, int d); static int compare_distance(int next, int current); static size_t get_position(void* vd); static void set_position(void *vd, size_t position); }; int node::get_distance(void* vd) { return reinterpret_cast<node*>(vd)->f_score_; } void node::set_distance(void* vd, int d) { reinterpret_cast<node*>(vd)->f_score_ = d; } int node::compare_distance(int next, int current) { return current < next; } size_t node::get_position(void* vd) { return reinterpret_cast<node*>(vd)->pqueue_pos_; } void node::set_position(void *vd, size_t position) { reinterpret_cast<node*>(vd)->pqueue_pos_ = position; } unsigned long long exchange_tile(int i, int j, unsigned long long state) { const unsigned long long mask = 0xfULL; unsigned long long nr_i = (state & mask << (i * 4)) >> (i * 4); unsigned long long nr_j = (state & mask << (j * 4)) >> (j * 4); state &= ~(mask << (i * 4)); state &= ~(mask << (j * 4)); state |= nr_i << (j * 4); state |= nr_j << (i * 4); return state; } void add_or_update_node(int mi, int ti, unsigned long long current, int g_score, map<unsigned long long, unsigned long long>& came_from, pqueue_t* open_set, map<unsigned long long, node*>& in_open_set, map<unsigned long long, node*>& removed_from_open_set) { node* pn = NULL; int tentative_g_score = g_score + 1; unsigned long long neighbor = exchange_tile(mi, ti, current); // move the missing tile map<unsigned long long, node*>::iterator i; if ((i = removed_from_open_set.find(neighbor)) != removed_from_open_set.end()) { // if neighbor has ever been in openset, but currenty it's not in openset pn = i->second; if (tentative_g_score < pn->g_score_) { // neighbor is added only if it has a lesser g_score than // it has ever have at any previous iterations pn->g_score_ = tentative_g_score; pn->f_score_ = pn->g_score_ + pn->h_score_; pqueue_insert(open_set, pn); // add neighbor to openset again in_open_set.insert(make_pair(neighbor, pn)); removed_from_open_set.erase(i); came_from[neighbor] = current; } } else if ((i = in_open_set.find(neighbor)) == in_open_set.end()) { // else if neighbor has never been in openset pn = new node(ti, neighbor, tentative_g_score); pqueue_insert(open_set, pn); // add neighbor to openset in_open_set.insert(make_pair(neighbor, pn)); came_from[neighbor] = current; } else { // neighbor is currently in openset pn = i->second; if (tentative_g_score < pn->g_score_) { pn->g_score_ = tentative_g_score; pn->f_score_ = pn->g_score_ + pn->h_score_; pqueue_change_priority(open_set, pn->f_score_, pn); came_from[neighbor] = current; } } } void free_nodes(const map<unsigned long long, node*>& nodes) { for (map<unsigned long long, node*>::const_iterator i = nodes.begin(), e = nodes.end(); i != e; ++i) delete i->second; } int a_star_seach(int mi, unsigned long long current, map<unsigned long long, unsigned long long>& came_from) { const int nr_initial_nodes = 1048576; pqueue_t* open_set = pqueue_init(nr_initial_nodes, node::compare_distance, node::get_distance, node::set_distance, node::get_position, node::set_position); // The set of tentative nodes to be evaluated, // initially containing the start node. map<unsigned long long, node*> in_open_set; // key is a state, value is the node instance that is in openset map<unsigned long long, node*> removed_from_open_set; // key is a state, value is the node instance that has ever been in openset // but it's currenty removed form it node* pn = new node(mi, current, 0); // start node pqueue_insert(open_set, pn); in_open_set.insert(make_pair(current, pn)); came_from[current] = -1; int shortest_g_score = -1; while (pqueue_size(open_set)) { // while openset is not empty pn = reinterpret_cast<node*>(pqueue_pop(open_set)); // the node in openset having the lowest f_score_ value // remove current from openset int mi = pn->mi_; unsigned long long current = pn->state_; int g_score = pn->g_score_; in_open_set.erase(current); removed_from_open_set.insert(make_pair(current, pn)); if (current == goal) { shortest_g_score = g_score; break; } if (g_score < g_score_max) { // for each neighbor in neighbor_nodes(current) if (mi < 12) // move the missing tile down add_or_update_node(mi, mi + 4, current, g_score, came_from, open_set, in_open_set, removed_from_open_set); if (mi > 3) // move the missing tile up add_or_update_node(mi, mi - 4, current, g_score, came_from, open_set, in_open_set, removed_from_open_set); if ((mi % 4) < 3) // move the missing tile right add_or_update_node(mi, mi + 1, current, g_score, came_from, open_set, in_open_set, removed_from_open_set); if (mi % 4) // move the missing tile left add_or_update_node(mi, mi - 1, current, g_score, came_from, open_set, in_open_set, removed_from_open_set); } } pqueue_free(open_set); free_nodes(in_open_set); free_nodes(removed_from_open_set); return shortest_g_score; } void print_steps(unsigned long long ps, unsigned long long cs, map<unsigned long long, unsigned long long>& states, ostringstream& oss) { if (ps == -1) return; print_steps(states[ps], ps, states, oss); int phi = -1, chi = -1; for (int i = 0; phi == -1 || chi == -1; i++, ps >>= 4, cs >>= 4) { if (!(ps & 0xf)) phi = i; if (!(cs & 0xf)) chi = i; } int ud = chi / 4 - phi / 4; if (ud > 0) oss << 'D'; else if (ud < 0) oss << 'U'; else if (chi % 4 - phi % 4 > 0) oss << 'R'; else oss << 'L'; } #ifdef DEBUG void follow_steps(int mi, unsigned long long state, const string& s) { for (int i = 0; i < s.length(); i++) { int nmi; switch (s[i]) { case 'U': assert(mi > 3); nmi = mi - 4; break; case 'D': assert(mi < 12); nmi = mi + 4; break; case 'L': assert(mi % 4); nmi = mi - 1; break; case 'R': assert((mi % 4) < 3); nmi = mi + 1; break; } state = exchange_tile(mi, nmi, state); mi = nmi; } assert(state == goal); } #endif int main() { int n; cin >> n; #ifdef __ELAPSED_TIME__ clock_t start = clock(); #endif while (n--) { int tiles[nr_tiles]; unsigned long long current = 0; // puzzle state int mi; // index of the missing tile for (int i = 0; i < nr_tiles; i++) { unsigned long long nr; cin >> nr; tiles[i] = nr; current |= nr << (i * 4); if (!nr) mi = i; } int shortest_g_score = -1; map<unsigned long long, unsigned long long> came_from; // the map of navigated nodes. if (is_puzzle_solvable(tiles)) shortest_g_score = a_star_seach(mi, current, came_from); if (shortest_g_score != -1) { #ifdef DEBUG cout << "number of steps = " << shortest_g_score << endl; #endif ostringstream oss; print_steps(came_from[goal], goal, came_from, oss); cout << oss.str() << endl; #ifdef DEBUG follow_steps(mi, current, oss.str()); #endif } else cout << "This puzzle is not solvable.\n"; } #ifdef __ELAPSED_TIME__ cerr << "elapsed time = " << static_cast<double>(clock() - start) / CLOCKS_PER_SEC << " sec.\n"; #endif return 0; }
No comments:
Post a Comment