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: Accepted)

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

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

		//パズルを管理するクラ
		class Puzzle
		{
			public Puzzle ( int[] area, int emptyIndex )
			{
				this.Area = new int[area.Length];
				area.CopyTo (this.Area, 0);

				EmptyIndex = emptyIndex;
				ManhattanDistance = this.GetManhattanDistance ();
			}
			public Puzzle ( int[] area, int emptyIndex, int manhattanDistance )
			{
				this.Area = new int[area.Length];
				area.CopyTo (this.Area, 0);

				EmptyIndex = emptyIndex;
				ManhattanDistance = manhattanDistance;
			}
			public Puzzle ( Puzzle copy )
			{
				this.Area = new int[copy.Area.Length];
				copy.Area.CopyTo (this.Area, 0);

				this.EmptyIndex = copy.EmptyIndex;
				this.ManhattanDistance = copy.ManhattanDistance;
			}

			public int[] Area { private set; get; }
			public int EmptyIndex { private set; get; }
			public int ManhattanDistance { 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;
			}
		}

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

			public static int IterativeDeepening ( Puzzle puzzle )
			{

				for (Limit = puzzle.ManhattanDistance; Limit <= LIMIT; Limit++)
				{
					Puzzle state = new Puzzle (puzzle);

					if (DepthFirstSearch (0, -100, state))
					{
						return Limit;
					}
				}

				return int.MinValue;
			}

			private static bool DepthFirstSearch ( int depth, int previous, Puzzle original )
			{
				if (original.ManhattanDistance == 0)
				{
					return true;
				}

				if (Limit < depth + original.ManhattanDistance)
				{
					return false;
				}

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

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

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

					if (Math.Max (previous, r) - Math.Min (previous, r) == 2)
					{
						continue;
					}

					move = new Puzzle (original);

					int distanceSubtract = original.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 tmp = original.Area[slideX * Puzzle.Length + slideY];
					original.Area[slideX * Puzzle.Length + slideY] = original.Area[originalX * Puzzle.Length + originalY];
					original.Area[originalX * Puzzle.Length + originalY] = tmp;

					int emptyIndex = slideX * Puzzle.Length + slideY;

					original = new Puzzle (original.Area, emptyIndex, distanceSubtract);

					if (DepthFirstSearch (depth + 1, r, original))
					{
						return true;
					}

					original = new Puzzle (move);
				}

				return false;
			}

		}
	}
}