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.Linq;
using System.Collections.Generic;
public static class Extension
{
	public static void ForEach<T>(this IEnumerable<T> e, Action<T, int> action)
	{
		var i = 0;
		foreach (var element in e) action(element, i++);
	}

}


public class FifteenPuzzle
{
    public static void Main()
    {
        var inputs = Console.ReadLine().Split(' ').Select(int.Parse)
            .Concat(Console.ReadLine().Split(' ').Select(int.Parse))
            .Concat(Console.ReadLine().Split(' ').Select(int.Parse))
            .Concat(Console.ReadLine().Split(' ').Select(int.Parse))
            .Cast<int>()
            .ToArray();
        
        var count = 0;
        var boards = new [] { new Board(inputs) };
        
        while (!boards.Any(x => x.Nodes.All(node => node.IsCorrect())))
        {
            count++;
            boards = boards.SelectMany(x => x.Move(count))
                .ToArray();
        }
        
        Console.WriteLine(count);
    }
}

public class Board
{
    public Node[] Nodes { get; set; }
    static HashSet<long> hash = new HashSet<long>();
    public Board(IEnumerable<int> inputs)
    {
        Nodes = inputs.Select((x, i) => new Node((i + 1) % 16, x))
            .ToArray();
    }
    
    public Board() { }
    
    public Board[] Move(int count)
    {
        var empty = Nodes.First(x => x.Current == 0);
        var children = empty.AdjacentNumbers
            .Select(_ => Board.Clone(this))
            .ToArray();
        
        empty.AdjacentNumbers
            .ForEach((adj, i) =>
            {
                var board = children[i];
                var left = board.Nodes.First(x => x.Current == 0);
                var right = board.Nodes.First(x => x.CorrectNumber == adj);
                
                board.SwapCurrent(left, right);
                
                if (!hash.Add(board.GetHash()))
                    children[i] = null;
                else if (board.Nodes.Where(x => x.Current != 0).Sum(x => GetManhattanDistance(x.Current, x.CorrectNumber)) > 45 - count)
                    children[i] = null;
            });
        
        return children.Where(x => x != null).ToArray();
    }
    
    public void SwapCurrent(Node left, Node right)
    {
        var temp = left.Current;
        left.Current = right.Current;
        right.Current = temp;
    }
    
    private static Board Clone(Board board)
    {
        return new Board
        {
            Nodes = board.Nodes.Select(Node.Clone)
                .ToArray(),
        };
    }

    public long GetHash()
    {
        var code = Nodes.Select(x => x.Current).Aggregate((accum, x) => accum * 10 + x);
        return code;
    }
    
    private static int GetManhattanDistance(int current, int correct)
    {
        return Math.Abs(current / 4 - correct / 4) + Math.Abs(current % 4 - correct % 4);
    }

    class ManhattanDistance
    {
        public int Correct { get; set; }
        public int Distance { get; set; }
        public int[] Currents { get; set; }
    }
}

public class Node
{
    private static readonly Dictionary<int, int[]> adjacentNumbersDic =
        new Dictionary<int, int[]>
        {
            { 1, new [] { 2, 4 } },
            { 2, new [] { 1, 3, 6 } },
            { 3, new [] { 2, 4, 7 } },
            { 4, new [] { 3, 8 } },
            { 5, new [] { 1, 5, 9 } },
            { 6, new [] { 2, 5, 7, 10 } },
            { 7, new [] { 3, 6, 8, 11 } },
            { 8, new [] { 4, 7, 12 } },
            { 9, new [] { 5, 10, 13 } },
            { 10, new [] { 6, 9, 11, 14 } },
            { 11, new [] { 7, 10, 12, 15 } },
            { 12, new [] { 8, 11, 0 } },
            { 13, new [] { 9, 14 } },
            { 14, new [] { 10, 13, 15 } },
            { 15, new [] { 11, 14, 0 } },
            { 0, new [] { 12, 15 } },
        };

    public int CorrectNumber { get; private set; }    
    public int Current { get; set; }
    public int[] AdjacentNumbers { get; set; }

    public Node(int correct, int current)
    {
        CorrectNumber = correct;
        Current = current;
        AdjacentNumbers = adjacentNumbersDic[correct];
    }
    
    public static Node Clone(Node node)
    {
        return new Node(node.CorrectNumber, node.Current);
    }
    
    public bool IsCorrect() { return CorrectNumber == Current; }
}