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

8 Puzzle

The goal of the 8 puzzle problem is to complete pieces on $3 \times 3$ 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 8 as shown below.

1 3 0
4 2 5
7 8 6

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 0

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

Input

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

Output

Print the fewest steps in a line.

Constraints

There is a solution.

Sample Input

1 3 0
4 2 5
7 8 6

Sample Output

4

Solution in C# (judge verdict: Accepted)

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

namespace ALDS1_13_C
{
    public class Program

    {
        public static void Main(string[] args)
        {
            const int VSize = 3;
            const int HSize = 3;
            int[,] board = new int[VSize, HSize];
            NPuzzle np = new NPuzzle();

            for (int i = 0 ; i < VSize ; i++)
            {
                int[] tmp = ReadIntAr();
                for (int j = 0 ; j < HSize ; j++)
                {
                    board[i, j] = tmp[j];
                }
            }       
           Console.WriteLine(np.FindShortestRange(board));
        }

        static string ReadSt() { return Console.ReadLine(); }
        static int ReadInt() { return int.Parse(Console.ReadLine()); }
        static long ReadLong() { return long.Parse(Console.ReadLine()); }
        static double ReadDouble() { return double.Parse(Console.ReadLine()); }
        static string[] ReadStAr(char sep = ' ') { return Console.ReadLine().Split(sep); }
        static int[] ReadIntAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Split(sep), e => int.Parse(e)); }
        static long[] ReadLongAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Split(sep), e => long.Parse(e)); }
        static double[] ReadDoubleAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Split(sep), e => double.Parse(e)); }

    }

}

namespace CompProgLib
{
    public class NPuzzle
    {
        public int VSize;
        public int HSize;
        public const int MAX_DISTANCE = 45;

        public class Node : IComparable<Node>
        {

            public int[,] Board { get; private set; }
            public int Distance { get; private set; }
            public int EvaluationValue { get; private set; }

            public Node(int[,] board, int distance,int evaluationValue)
            {
                Board = board;
                Distance = distance;
                EvaluationValue = evaluationValue;
            }

            public int CompareTo(Node other)
            {
                return -1 * ((EvaluationValue+ Distance) - (other.EvaluationValue+ other.Distance));
                //return -1 * ((int)(Math.Pow(EvaluationValue, 0.5) + Distance) - ((int)Math.Pow(other.EvaluationValue, 0.5) + other.Distance)); 
                //return -1 * ((EvaluationValue) - (other.EvaluationValue));
                //return -1 * ((Distance) - (other.Distance));
            }

        }

        public int FindShortestRange(int[,] board)
        {

            //出現した盤面の記録
            HashSet<int> visited = new HashSet<int>();

            PriorityQueue<Node> pq = new PriorityQueue<Node>();
            Node start = new Node(board, 0,GetEvaluationValue(board));
            visited.Add(GetHash(board));
            pq.Enqueue(start);

            while (pq.Count > 0)
            {
                Node tmp = pq.Dequeue();
                for (int i = 0 ; i < tmp.Board.GetLength(0) ; i++)
                {
                    for (int j = 0 ; j < tmp.Board.GetLength(1) ; j++)
                    {
                        if (tmp.Board[i, j] == 0)
                        {
                            //左移動
                            if (j != 0)
                            {
                                int[,] newBoard = (int[,])tmp.Board.Clone();
                                newBoard[i, j] = newBoard[i, j - 1];
                                newBoard[i, j - 1] = 0;
                                int Hash = GetHash(newBoard);
                                int evaluationValue = GetEvaluationValue(newBoard);
                                if (!visited.Contains(Hash) && tmp.Distance + evaluationValue <= MAX_DISTANCE)
                                {
                                    visited.Add(Hash);
                                    pq.Enqueue(new Node(newBoard, tmp.Distance + 1, evaluationValue));
                                }
                            }

                            //右移動
                            if (j != tmp.Board.GetLength(1) - 1)
                            {
                                int[,] newBoard = (int[,])tmp.Board.Clone();
                                newBoard[i, j] = newBoard[i, j + 1];
                                newBoard[i, j + 1] = 0;
                                int Hash = GetHash(newBoard);
                                int evaluationValue = GetEvaluationValue(newBoard);
                                if (!visited.Contains(Hash) && tmp.Distance + evaluationValue <= MAX_DISTANCE)
                                {
                                    visited.Add(Hash);
                                    pq.Enqueue(new Node(newBoard, tmp.Distance + 1, evaluationValue));
                                }
                            }

                            //上移動
                            if (i != 0)
                            {
                                int[,] newBoard = (int[,])tmp.Board.Clone();
                                newBoard[i, j] = newBoard[i - 1, j];
                                newBoard[i - 1, j] = 0;
                                int Hash = GetHash(newBoard);
                                int evaluationValue = GetEvaluationValue(newBoard);
                                if (!visited.Contains(Hash) && tmp.Distance + evaluationValue <= MAX_DISTANCE)
                                {
                                    visited.Add(Hash);
                                    pq.Enqueue(new Node(newBoard, tmp.Distance + 1, evaluationValue));
                                }
                            }

                            //下移動
                            if (i != tmp.Board.GetLength(0) - 1)
                            {
                                int[,] newBoard = (int[,])tmp.Board.Clone();
                                newBoard[i, j] = newBoard[i + 1, j];
                                newBoard[i + 1, j] = 0;
                                int Hash = GetHash(newBoard);
                                int evaluationValue = GetEvaluationValue(newBoard);
                                if (!visited.Contains(Hash) && tmp.Distance + evaluationValue <= MAX_DISTANCE)
                                {
                                    visited.Add(Hash);
                                    pq.Enqueue(new Node(newBoard, tmp.Distance + 1, evaluationValue));
                                }
                            }
                        }
                    }
                }

                if (tmp.EvaluationValue == 0)
                {
                    return tmp.Distance;
                }

            }
            return 0;
        }

        private int GetEvaluationValue(int[,] board)
        {
            int res = 0;
            for (int i = 0 ; i < board.GetLength(0) ; i++)
            {
                for (int j = 0 ; j < board.GetLength(1) ; j++)
                {
                    if (board[i, j] != 0)
                    {
                        res += Math.Abs((board[i, j] - 1) / board.GetLength(1) - i);//縦方向
                        res += Math.Abs((board[i, j] - 1) % board.GetLength(1) - j);//横方向
                    }
                }
            }
            return res;
        }

        private int GetHash(int[,] board)
        {

            string tmp = "";
            for (int i = 0 ; i < board.GetLength(0) ; i++)
            {
                for (int j = 0 ; j < board.GetLength(1) ; j++)
                {
                    tmp += board[i, j].ToString("0000");
                }
            }
            return tmp.GetHashCode();
           // return board.ToString().GetHashCode();
        }
    }
}

namespace CompProgLib
{
    public class PriorityQueue<T> where T : IComparable<T>

    {

        private List<T> Buffer { get; set; }

        public int Count { get { return Buffer.Count; } }

        public PriorityQueue() { Buffer = new List<T>(); }
        public PriorityQueue(int capacity) { Buffer = new List<T>(capacity); }


        /// <summary>
        /// ヒープ化されている配列リストに新しい要素を追加する。
        /// </summary>
        public void Enqueue(T item)
        {
            int n = Buffer.Count;
            Buffer.Add(item);

            while (n != 0)
            {
                int i = (n - 1) / 2;
                if (Buffer[n].CompareTo(Buffer[i]) > 0)
                {
                    T tmp = Buffer[n]; Buffer[n] = Buffer[i]; Buffer[i] = tmp;
                }
                n = i;
            }
        }

        /// <summary>
        /// ヒープから最大値を取り出し、削除する。
        /// </summary>
        public T Dequeue()
        {
            T ret = Buffer[0];
            int n = Buffer.Count - 1;
            Buffer[0] = Buffer[n];
            Buffer.RemoveAt(n);

            for (int i = 0, j ; (j = 2 * i + 1) < n ;)
            {
                if ((j != n - 1) && (Buffer[j].CompareTo(Buffer[j + 1]) < 0))
                    j++;
                if (Buffer[i].CompareTo(Buffer[j]) < 0)
                {
                    T tmp = Buffer[j]; Buffer[j] = Buffer[i]; Buffer[i] = tmp;
                }
                i = j;
            }
            return ret;
        }

        /// <summary>
        /// ヒープから最大値を参照する。
        /// </summary>
        public T Peek()
        {
            if (Count == 0) throw new InvalidOperationException();
            return this.Buffer[0];
        }
    }
}