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;

namespace ConsoleApplication1
{
    class Puzzle : IComparable
    {
        internal int[] f;
        internal int space, MD, cost;

        public Puzzle(int n)
        {
            f = new int[n];
            space = MD = cost = default(int);
        }

        public Puzzle(Puzzle source)
        {
            f = (int[])source.f.Clone();
            space = source.space;
            MD = source.MD;
            cost = source.cost;
        }

        public override int GetHashCode()
        {
            return MakeString(f).GetHashCode();
        }

        public override bool Equals(object obj)
        {
            Puzzle other = (Puzzle)obj;
            return MakeString(f) == MakeString(other.f);
        }

        public int CompareTo(object obj)
        {
            Puzzle other = (Puzzle)obj;
            return (other.cost + other.MD) - (cost + MD);
        }

        private string MakeString(int[] arr)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var item in arr) sb.Append(item);
            return sb.ToString();
        }
    }

    class NPuzzle
    {
        readonly int n, h, w;

        readonly int[] moveX = new int[] { 0, -1, 0, +1 };
        readonly int[] moveY = new int[] { -1, 0, +1, 0 };
        //readonly char[] dir = new char[] { 'u', 'l', 'd', 'r' };

        int[,] MDT;

        public NPuzzle(int h, int w)
        {
            this.h = h;
            this.w = w;
            n = h * w;

            MDT = new int[n, n];

            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    MDT[i, j] = Math.Abs(i / h - j / w) + Math.Abs(i % h - j % w);
        }

        public int Astar(Puzzle p)
        {
            PriorityQueue<Puzzle> PQ = new PriorityQueue<Puzzle>();
            p.MD = GetAllMD(p);

            HashSet<Puzzle> V = new HashSet<Puzzle>();

            PQ.Enqueue(p);

            Puzzle u, v;

            while (PQ.Count() > 0)
            {
                u = PQ.Dequeue();

                if (u.MD == 0) return u.cost;

                V.Add(u);

                int sx = u.space % w;
                int sy = u.space / w;

                for (int r = 0; r < 4; r++)
                {
                    int tx = sx + moveX[r];
                    int ty = sy + moveY[r];

                    if (!IsValid(tx, ty)) continue;

                    v = new Puzzle(u);

                    int indexT = tx + ty * w;
                    int indexS = sx + sy * w;

                    v.MD -= MDT[indexT, v.f[indexT] - 1];
                    v.MD += MDT[indexS, v.f[indexT] - 1];

                    int temp = v.f[indexT];
                    v.f[indexT] = v.f[indexS];
                    v.f[indexS] = temp;

                    v.space = indexT;

                    if (!V.Contains(v))
                    {
                        v.cost++;
                        PQ.Enqueue(v);
                    }
                }
            }
            return -1;
        }

        private bool IsValid(int x, int y)
        {
            if (x < 0) return false;
            if (y < 0) return false;
            if (x >= w) return false;
            if (y >= h) return false;
            return true;
        }

        private int GetAllMD(Puzzle p)
        {
            int sum = 0;

            for (int i = 0; i < n; i++)
            {
                if (p.f[i] != n) sum += MDT[i, p.f[i] - 1];
            }
            return sum;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            int h = 3, w = 3;
            int n = h * w;

            NPuzzle NP = new NPuzzle(h, w);
            Puzzle s = new Puzzle(n);

            for (int i = 0; i < h; i++)
            {
                int[] input = Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);

                for (int j = 0; j < w; j++)
                {
                    int index = i * w + j;
                    s.f[index] = input[j];

                    if (input[j] == 0)
                    {
                        s.f[index] = n;
                        s.space = index;
                    }
                }
            }
            Console.WriteLine(NP.Astar(s));
        }
    }

    class PriorityQueue<T> where T : IComparable
    {
        List<T> heap;
        int size;

        public PriorityQueue() { heap = new List<T>(); }

        public void Enqueue(T x)
        {
            heap.Add(x);
            int i = size++;

            while (i > 0)
            {
                int p = (i - 1) / 2;

                if (Compare(heap[p], x) <= 0) break;

                heap[i] = heap[p];
                i = p;
            }
            heap[i] = x;
        }

        public T Dequeue()
        {
            T ret = heap[0];
            T x = heap[--size];

            int i = 0;
            while (i * 2 + 1 < size)
            {
                int a = i * 2 + 1;
                int b = i * 2 + 2;

                if (b < size && Compare(heap[b], heap[a]) < 0)
                    a = b;
                if (Compare(heap[a], x) >= 0)
                    break;

                heap[i] = heap[a];
                i = a;
            }
            heap[i] = x;
            heap.RemoveAt(size);
            return ret;
        }

        public int Count() { return size; }

        private int Compare(T x, T y) { return y.CompareTo(x); }
    }
}