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# (judge verdict: Time Limit Exceeded)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ALDS1_13_C_15_Puzzle
{
	class Program
	{
		public static int[,] ManhattanDistanceTable = new int[Puzzle.Length2, Puzzle.Length2];

		static void Main ( string[] args )
		{
			for (int lp = 0; lp < Puzzle.Length2; lp++)
			{
				for (int lp2 = 0; lp2 < Puzzle.Length2; lp2++)
				{
					ManhattanDistanceTable[lp, lp2] =
						Math.Abs (lp / Puzzle.Length - lp2 / Puzzle.Length) +
						Math.Abs (lp % Puzzle.Length - lp2 % Puzzle.Length);
				}
			}

			List<int> input = new List<int> (Puzzle.Length2);
			for (int lp = 0; lp < 4; lp++)
			{
				int[] a = Array.ConvertAll (Console.ReadLine ().Split (' '), int.Parse);
				input.AddRange (a);
			}

			int index = Array.IndexOf (input.ToArray (), 0);
			input[index] = Puzzle.Length2;

			Puzzle i = new Puzzle (input.ToArray (), index, 0);

			var result = OperatePuzzle.AStar (i);
			Console.WriteLine (result);
		}

		//パズルを管理するクラス
		class Puzzle
		{
			public Puzzle ( int[] area, int emptyIndex, int cost )
			{
				Area = area;
				EmptyIndex = emptyIndex;
				ManhattanDistance = this.GetManhattanDistance ();
				Cost = cost;
			}
			public Puzzle ( int[] area, int emptyIndex, int manhattanDistance, int cost )
			{
				Area = area;
				EmptyIndex = emptyIndex;
				ManhattanDistance = manhattanDistance;
				Cost = cost;
			}
			public Puzzle ( Puzzle copy )
			{
				this.Area = new int[Length2];
				copy.Area.CopyTo (this.Area, 0);
				this.EmptyIndex = copy.EmptyIndex;
				this.ManhattanDistance = copy.ManhattanDistance;
				this.Cost = copy.Cost;
			}

			public int[] Area { private set; get; }
			public int EmptyIndex { private set; get; }
			public int ManhattanDistance { private set; get; }
			public int Cost { private set; get; }

			public const int Length = 4;
			public const int Length2 = 16;

			private int GetManhattanDistance ()
			{
				int sum = 0;

				for (int lp = 0; lp < Puzzle.Length2; lp++)
				{
					if (Area[lp] != Puzzle.Length2)
					{
						sum += ManhattanDistanceTable[lp, Area[lp] - 1];
					}
				}

				return sum;
			}
		}

		class Equal : EqualityComparer<Puzzle>
		{
			public override bool Equals ( Puzzle puzzle1, Puzzle puzzle2 )
			{
				return puzzle1.Area.SequenceEqual (puzzle2.Area);
			}
			public override int GetHashCode ( Puzzle item )
			{
				return
					item.Area[0] +
					item.Area[1] * 10 +
					item.Area[2] * 100 +
					item.Area[3] * 1000 +
					item.Area[4] * 10000 +
					item.Area[5] * 100000 +
					item.Area[6] * 1000000 +
					item.Area[7] * 10000000 +
					item.Area[8] * 100000000 +
					item.Area[9] * 1000000000;
			}
		}

		class PuzzleState
		{
			public PuzzleState ( Puzzle puzzle, int estimated )
			{
				Puzzle = puzzle;
				Estimated = estimated;
			}

			public Puzzle Puzzle { private set; get; }
			public int Estimated { private set; get; }

			public static bool operator < ( PuzzleState state1, PuzzleState state2 )
			{
				return state1.Estimated < state2.Estimated;
			}
			public static bool operator > ( PuzzleState state1, PuzzleState state2 )
			{
				return state1.Estimated > state2.Estimated;
			}
		}

		//パズルを操作するクラス
		static class OperatePuzzle
		{
			private static int[] moveX = { 0, -1, 0, 1 };
			private static int[] moveY = { 1, 0, -1, 0 };

			public static int AStar ( Puzzle puzzle )
			{
				PriorityQueue pq = new PriorityQueue (1000);

				Equal equal = new Equal ();
				Dictionary<Puzzle, bool> puzzleCase = new Dictionary<Puzzle, bool> (1000, equal);

				Puzzle original = null;
				Puzzle move = null;

				PuzzleState initial = new PuzzleState (puzzle, puzzle.ManhattanDistance);
				pq.Insert (initial);

				while (!pq.IsEmpty ())
				{
					PuzzleState st = pq.ExtractMin ();
					original = st.Puzzle;

					if (original.ManhattanDistance == 0)
					{
						return original.Cost;
					}

					if (!puzzleCase.ContainsKey (original))
					{
						puzzleCase.Add (original, true);
					}

					int originalX = original.EmptyIndex / Puzzle.Length;
					int originalY = original.EmptyIndex % Puzzle.Length;

					for (int lp = 0; lp < 4; lp++)
					{
						int slideX = originalX + moveX[lp];
						int slideY = originalY + moveY[lp];

						if (slideX < 0 || slideY < 0 || Puzzle.Length <= slideX || Puzzle.Length <= slideY)
						{
							continue;
						}

						move = new Puzzle (original);

						int tmp = move.Area[slideX * Puzzle.Length + slideY];
						move.Area[slideX * Puzzle.Length + slideY] = move.Area[originalX * Puzzle.Length + originalY];
						move.Area[originalX * Puzzle.Length + originalY] = tmp;

						if (!puzzleCase.ContainsKey (move))
						{
							int emptyIndex = slideX * Puzzle.Length + slideY;

							int distanceSubtract = move.ManhattanDistance;
							distanceSubtract -= ManhattanDistanceTable[slideX * Puzzle.Length + slideY, original.Area[slideX * Puzzle.Length + slideY] - 1];
							distanceSubtract += ManhattanDistanceTable[originalX * Puzzle.Length + originalY, original.Area[slideX * Puzzle.Length + slideY] - 1];

							int cost = move.Cost + 1;

							move = new Puzzle (move.Area, emptyIndex, distanceSubtract, cost);

							PuzzleState news = new PuzzleState (move, cost + distanceSubtract);
							pq.Insert (news);
						}
					}
				}

				return -1;
			}
		}

		class PriorityQueue
		{
			public PriorityQueue ( int capacity )
			{
				this.NumArray = new PuzzleState[capacity + 1];
				this.ArrayIndex = 0;
			}

			private PuzzleState[] NumArray { set; get; }
			private int ArrayIndex { set; get; }

			public void Insert ( PuzzleState value )
			{
				ArrayIndex++;

				if (NumArray.Length <= ArrayIndex)
				{
					PuzzleState[] tmp = new PuzzleState[NumArray.Length];
					NumArray.CopyTo (tmp, 0);
					NumArray = new PuzzleState[NumArray.Length + 1];
					tmp.CopyTo (NumArray, 0);
				}

				IncreaseKey (ArrayIndex, value);
			}

			public PuzzleState ExtractMin ()
			{
				if (ArrayIndex < 1)
				{
					return null;
				}

				PuzzleState min = NumArray[1];

				NumArray[1] = NumArray[ArrayIndex];
				ArrayIndex--;

				MinHeapify (1);

				return min;
			}

			public bool IsEmpty ()
			{
				return this.ArrayIndex <= 0;
			}

			private void IncreaseKey ( int index, PuzzleState value )
			{
				NumArray[index] = value;

				while (1 < index && NumArray[index].Estimated < NumArray[ParentKey (index)].Estimated)
				{
					int p = ParentKey (index);

					var tmp = NumArray[p];
					NumArray[p] = NumArray[index];
					NumArray[index] = tmp;

					index = p;
				}
			}

			private void MinHeapify ( int index )
			{
				int leftIndex = LeftKey (index);
				int rightindex = RightKey (index);
				int maxIndex;

				if (leftIndex <= ArrayIndex && NumArray[leftIndex].Estimated < NumArray[index].Estimated)
				{
					maxIndex = leftIndex;
				}
				else
				{
					maxIndex = index;
				}

				if (rightindex <= ArrayIndex && NumArray[rightindex].Estimated < NumArray[maxIndex].Estimated)
				{
					maxIndex = rightindex;
				}

				if (maxIndex != index)
				{
					var tmp = NumArray[index];
					NumArray[index] = NumArray[maxIndex];
					NumArray[maxIndex] = tmp;

					MinHeapify (maxIndex);
				}
			}

			private int ParentKey ( int index )
			{
				return index / 2;
			}
			private int LeftKey ( int index )
			{
				return index * 2;
			}
			private int RightKey ( int index )
			{
				return index * 2 + 1;
			}
		}
	}
}