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: Wrong Answer)

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

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

			Puzzle i = new Puzzle ();
			i.Area = new int[Puzzle.Length2];

			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;
			input.CopyTo (i.Area);
			i.EmptyIndex = index;

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

		public static int[,] MDT = new int[Puzzle.Length2, Puzzle.Length2];

		//パズルを管理するクラス
		struct Puzzle
		{
			public Puzzle ( int[] area, int emptyIndex, int manhattanDistance, int cost )
			{
				Area = area;
				EmptyIndex = emptyIndex;
				ManhattanDistance = manhattanDistance;
				Cost = cost;
			}

			public int[] Area;
			public int EmptyIndex;
			public int ManhattanDistance;
			public int Cost;

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

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

			public Puzzle Puzzle;
			public int Estimated;

			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[] Dx = { 0, -1, 0, 1 };
			private static int[] Dy = { 1, 0, -1, 0 };
			//private static char[] Direct = { 'r', 'u', 'l', 'd' };

			public static int GetManhattanDistance ( Puzzle puzzle )
			{
				int sum = 0;														 

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

				return sum;
			}

			public static int AStar ( Puzzle puzzle )
			{
				PriorityQueue pq = new PriorityQueue (1000);
				puzzle.ManhattanDistance = GetManhattanDistance (puzzle);
				puzzle.Cost = 0;

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

				Puzzle u, v;
				u.Area = new int[Puzzle.Length2];
				v.Area = new int[Puzzle.Length2];

				PuzzleState initial;
				initial.Puzzle = puzzle;
				initial.Estimated = GetManhattanDistance (puzzle);
				pq.Insert (initial);

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

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

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

					int sx = u.EmptyIndex / Puzzle.Length;
					int sy = u.EmptyIndex % Puzzle.Length;

					for (int lp = 0; lp < 4; lp++)
					{
						int tx = sx + Dx[lp];
						int ty = sy + Dy[lp];

						if (tx < 0 || ty < 0 || tx >= Puzzle.Length || ty >= Puzzle.Length)
						{
							continue;
						}

						v = u;

						v.ManhattanDistance -= MDT[tx * Puzzle.Length + ty, v.Area[tx * Puzzle.Length + ty] - 1];
						v.ManhattanDistance += MDT[sx * Puzzle.Length + sy, v.Area[tx * Puzzle.Length + ty] - 1];

						var tmp = v.Area[tx * Puzzle.Length + ty];
						v.Area[tx * Puzzle.Length + ty] = v.Area[sx * Puzzle.Length + sy];
						v.Area[sx * Puzzle.Length + sy] = tmp;

						v.EmptyIndex = tx * Puzzle.Length + ty;

						if (!puzzleCase.ContainsKey (v))
						{
							v.Cost++;
							PuzzleState news;
							news.Puzzle = v;
							news.Estimated = v.Cost + v.ManhattanDistance;
							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);
				}

				NumArray[ArrayIndex] = new PuzzleState ();
				IncreaseKey (ArrayIndex, value);
			}

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

				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 )
			{
				if (value < NumArray[index])
				{
					return;
				}

				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;
			}
		}
	}
}