Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

15 Puzzle

The goal of the 15 puzzle problem is to complete pieces on $4 \times 4$ cells where one of the cells is empty space.

In this problem, the space is represented by 0 and pieces are represented by integers from 1 to 15 as shown below.

1 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15

You can move a piece toward the empty space at one step. Your goal is to make the pieces the following configuration in the shortest move (fewest steps).

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 0

Write a program which reads an initial state of the puzzle and prints the fewest steps to solve the puzzle.

Input

The $4 \times 4$ integers denoting the pieces or space are given.

Output

Print the fewest steps in a line.

Constraints

The given puzzle is solvable in at most 45 steps.

Sample Input

1 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15

Sample Output

8

Solution in C++14 (judge verdict: Time Limit Exceeded)

#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <stdlib.h>
#include <limits.h>
#include <unordered_map>
#include <string>
#include <map>
#include <sstream>
#include <cstdlib> 
using namespace std;

#define INF     INT_MAX
#define SIZE  16
#define WIDTH 4

#define LEFT  0
#define RIGHT 1
#define UP    2
#define DOWN  3

typedef struct{
  int            dist;        // accumulated distance fromt start board 
  int            evaluation;
  bool           visited;     // default: false
} v_t;

map< string, v_t >  Node; // Key: cell hash, Value: node
void assert(vector<int> v);

bool Move[SIZE][4];
void initMove(){
  // Can Move Right?
  Move[0][RIGHT]=Move[1][RIGHT]=Move[2][RIGHT]=true;
  Move[4][RIGHT]=Move[5][RIGHT]=Move[6][RIGHT]=true;
  Move[8][RIGHT]=Move[9][RIGHT]=Move[10][RIGHT]=true;
  Move[12][RIGHT]=Move[13][RIGHT]=Move[14][RIGHT]=true;
  // Can Move Left?
  Move[1][LEFT]=Move[2][LEFT]=Move[3][LEFT]=true;
  Move[5][LEFT]=Move[6][LEFT]=Move[7][LEFT]=true;
  Move[9][LEFT]=Move[10][LEFT]=Move[11][LEFT]=true;
  Move[13][LEFT]=Move[14][LEFT]=Move[15][LEFT]=true;
  // Can Move Up?
  Move[4][UP]=Move[5][UP]=Move[6][UP]=Move[7][UP]=true;
  Move[8][UP]=Move[9][UP]=Move[10][UP]=Move[11][UP]=true;
  Move[12][UP]=Move[13][UP]=Move[14][UP]=Move[15][UP]=true;
  // Can Move Down?
  Move[0][DOWN]=Move[1][DOWN]=Move[2][DOWN]=Move[3][DOWN]=true;
  Move[4][DOWN]=Move[5][DOWN]=Move[6][DOWN]=Move[7][DOWN]=true;
  Move[8][DOWN]=Move[9][DOWN]=Move[10][DOWN]=Move[11][DOWN]=true;
}

multimap< int, string > Q;   // Key: dist + evaluation , Value: cell hash
string hashkey(vector<int> cell){
  string hash;
  for(const auto& e: cell){
    hash += to_string(e) + ",";
  }
  return hash;
}

vector<string> split(const string &str, char sep)
{
  vector<string> v;
  stringstream ss(str);
  string buffer;
  while( getline(ss, buffer, sep) ) {
      v.push_back(buffer);
  }
  return v;
}
vector<int> dehash(string hash){
  vector<int> cell;
  vector<string> board = split(hash, ',');
  for(const auto& e : board){
    cell.push_back(stoi(e));
  }
  return cell;
}

int estimateH(vector<int> cell){
  int hr = 0; // heuristic
  for(int i = 0 ; i < SIZE ; i++){
    if(cell[i] == 0){
      cell[i] = SIZE;
    }
    int rowhr = abs( (cell[i]-1)%WIDTH - i%WIDTH );
    int colhr = abs( (cell[i]-1)/WIDTH - i/WIDTH );
    hr += rowhr + colhr;
  }
  hr = (int)(hr*0.85);
	return hr;
}

vector<int> updateGQ(vector<int> curCell,int space, int dir, string curHash){
  vector<int> nextCell(curCell);
  int moveto = space + dir; // move to = space(0) + direction
  nextCell[space] = nextCell[moveto];
  nextCell[moveto] = 0;
  const string nextHash = hashkey(nextCell); 
  if( !Node[nextHash].visited ){
    Node[nextHash].visited = true;
  	Node[nextHash].dist = Node[curHash].dist + 1;
   	Node[nextHash].evaluation = estimateH(nextCell);
    int dist_ev = Node[nextHash].dist + Node[nextHash].evaluation;
		Q.insert(make_pair(dist_ev, nextHash));
  }
  return nextCell;
}

void move(vector<int> curCell, string curHash){
  for(int i = 0 ; i < SIZE ; i++ ){
    vector<int> resultCell;
    if(curCell[i] == 0){
      if(Move[i][LEFT]){
        resultCell = updateGQ(curCell, i, -1, curHash); 
      }
      if(Move[i][RIGHT]){
        resultCell = updateGQ(curCell, i, 1, curHash); 
      }
      if(Move[i][UP]){
        resultCell = updateGQ(curCell, i, -4, curHash); 
      }
      if(Move[i][DOWN]){
        resultCell = updateGQ(curCell, i, 4, curHash); 
      }
    }
  }
  return;
}

void assert(vector<int> v){ // for debug
  int i = 0;
	for(auto itr = v.begin() ; itr != v.end(); ++itr){
    fprintf(stderr, "%d "  ,*itr);
    i++;
    if(i==WIDTH){
      i = 0;
      fprintf(stderr, "\n");
    }
  }
}

int main(){ // Dijkstra P.254
  vector<int> cell(SIZE); // current cell
  const vector<int> goalCell = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
  const string goalHash = hashkey(goalCell);
  initMove();
 
  //  insert First node
  for(int i = 0 ; i < SIZE ; i++){
    int num;
    scanf("%d" ,&num);
    cell[i] = num;
  }
  v_t firstNode = {0, 0, true};
  Node[hashkey(cell)] = firstNode;
	Q.insert( make_pair(0, hashkey(cell)) );
  move(cell, hashkey(cell));
  auto itr = Q.begin();
  Q.erase(itr);

  while(!Q.empty()){
    if( Node[goalHash].visited ){
      break;
    }
    auto itr = Q.begin();
    string curHash = itr->second;
    Q.erase(itr);
    cell = dehash(curHash);
    move(cell,curHash);
  }

  printf("%d\n" ,Node[goalHash].dist );
  return 0;
}