let (n1, n2, n_limit) = (4, 16, 45)

let area =
  [| [1; 4]; [0; 2; 5]; [1; 3; 6]; [2; 7]
   ; [0; 5; 8]; [1; 4; 6; 9]; [2; 5; 7; 10]; [3; 6; 11]
   ; [4; 9; 12]; [5; 8; 10; 13]; [6; 9; 11; 14]; [7; 10; 15]
   ; [8; 13]; [9; 12; 14]; [10; 13; 15]; [11; 14] |]

let goal = Array.init n2 (fun i -> (i + 1) mod n2)

let md = Array.make_matrix n2 n2 0

let initialize_md () =
  for i = 0 to n2 - 2 do
    for j = 0 to n2 - 1 do
      md.(i+1).(j) <- abs (i / n1 - j / n1) + abs (i mod n1 - j mod n1)
    done
  done

let idastar a limit space lower =
  let rec doit i space moved lower =
    if i = limit then
      if a <> goal then ()
      else begin
        Printf.printf "%d\n" limit;
        exit 0;
      end
    else
      List.iter (fun j ->
        let x = a.(j) in
        if x = moved then ()
        else
          let lower = lower - md.(x).(j) + md.(x).(space) in
          if lower + i > limit then ()
          else begin
            a.(j) <- 0;
            a.(space) <- x;
            doit (i + 1) j x lower;
            a.(j) <- x;
            a.(space) <- 0;
          end)
      area.(space) in
  doit 0 space (-1) lower

let findi a x =
  let n = Array.length a in
  let rec doit i =
    if i = n then assert false
    else if a.(i) = x then i
    else doit (i + 1) in
  doit 0

let () =
  initialize_md ();
  let a = Array.init n2 (fun _ -> Scanf.scanf "%d " (fun i -> i)) in
  let space = findi a 0 in
  let lower =
    Array.fold_left (fun (i, sum) e -> (i + 1, sum + md.(e).(i))) (0, 0) a |> snd in
  for limit = lower to n_limit do
    idastar a limit space lower
  done