Problem p02246 (C++) — buggy submission, verdict: Runtime Error

// 15パズル ( 反復深化法 )
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <stack>

#include <map>
#include <queue>
#include <vector>

using namespace std;

#define N 4
#define N2 16
#define LIMIT 100

static const int dx[4] = {0, -1, 0, 1};
static const int dy[4] = {1, 0, -1, 0};
static const char dir[4] = {'r', 'u', 'l', 'd'};
int MDT[N2][N2];

struct Puzzle {
  int f[N2], space, MD;
};

Puzzle state;
// 深さの制限
int limit;
int path[LIMIT];

int getAllMD(Puzzle pz) {
  int sum = 0;

  for (int i = 0; i < N2; i++) {
    if (pz.f[i] == N2) {
      continue;
    }

    sum += MDT[i][pz.f[i] - 1];
  }

  return sum;
}

bool isSolved() {
  for (int i = 0; i < N2; i++) {
    if (state.f[i] != i + 1) {
      return false;
    }
  }

  return true;
}

bool dfs(int depth, int prev) {
  if (state.MD == 0) {
    return true;
  }

  // 現在の深さにヒューリスティックを足して制限を超えたら枝を刈る
  if (depth + state.MD > limit) {
    return false;
  }

  int sx = state.space / N;
  int sy = state.space % N;
  Puzzle tmp;

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

    if (tx < 0 || ty < 0 || tx >= N || ty >= N) {
      continue;
    }

    if (max(prev, r) - min(prev, r) == 2) {
      continue;
    }

    tmp = state;

    // マンハッタン距離の差分を計算しつつ、ピースをスワップ
    state.MD -= MDT[tx * N + ty][state.f[tx * N + ty] - 1];
    state.MD += MDT[sx * N + sy][state.f[tx * N + ty] - 1];
    swap(state.f[tx * N + ty], state.f[sx * N + sy]);
    state.space = tx * N + ty;

    if (dfs(depth + 1, r)) {
      path[depth] = r;
      return true;
    }

    state = tmp;
  }

  return false;
}

// 反復深化
string iterative_deepening(Puzzle in) {
  // 初期状態のマンハッタン距離
  in.MD = getAllMD(in);

  for (limit = in.MD; limit <= LIMIT; limit++) {
    state = in;

    if (dfs(0, -100)) {
      string ans = "";

      for (int i = 0; i < limit; i++) {
        ans += dir[path[i]];
      }

      return ans;
    }
  }

  return "unsolvable";
}

int main() {
  for (int i = 0; i < N2; i++) {
    for (int j = 0; j < N2; j++) {
      MDT[i][j] = abs(i / N - j / N) + abs(i % N - j % N);
    }
  }

  Puzzle in;

  for (int i = 0; i < N2; i++) {
    cin >> in.f[i];

    if (in.f[i] == 0) {
      in.f[i] = N2;
      in.space = i;
    }
  }

  string ans = iterative_deepening(in);
  cout << ans.size() << endl;

  return 0;
}


Error: 0

Fixed submission:

// 15パズル ( 山登り法 )
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <stack>

#include <map>
#include <queue>
#include <vector>

using namespace std;

// 15パズルのパネル枚数
const int PANEL_NUM = 16;

// 15パズルの最大手数
const int MAX_STEP = 80;

// 並び替えの目標の状態
int ary_Target[PANEL_NUM] = {1, 2,  3,  4,  5,  6,  7,  8,
                             9, 10, 11, 12, 13, 14, 15, 0};

// パネルの移動量を保持する構造体
typedef struct {
  int x;
  int y;
} st_move;

// パネルの移動座標を保持する配列
st_move move_p[] = {
    {-1, 0}, // 上
    {0, -1}, // 左
    {0, 1},  // 右
    {1, 0}   // 下
};

// パネルの移動状態を表現する列挙型
enum E_Move { UP = 0, LEFT, RIGHT, DOWN, MOVE_LAST };

// 各パネルを正解の位置まで動かすのに必要な最短距離
// 横方向 : パネルの位置を表す
// 縦方向 : パネルのNo.を表す
int moveDistance[PANEL_NUM][PANEL_NUM] = {
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // ダミー
    {0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6}, // 1
    {1, 0, 1, 2, 2, 1, 2, 3, 3, 2, 3, 4, 4, 3, 4, 5}, // 2
    {2, 1, 0, 1, 3, 2, 1, 2, 4, 3, 2, 3, 5, 4, 3, 4}, // 3
    {3, 2, 1, 0, 4, 3, 2, 1, 5, 4, 3, 2, 6, 5, 4, 3}, // 4
    {1, 2, 3, 4, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5}, // 5
    {2, 1, 2, 3, 1, 0, 1, 2, 2, 1, 2, 3, 3, 2, 3, 4}, // 6
    {3, 2, 1, 2, 2, 1, 0, 1, 3, 2, 1, 2, 4, 3, 2, 3}, // 7
    {4, 3, 2, 1, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, 3, 2}, // 8
    {2, 3, 4, 5, 1, 2, 3, 4, 0, 1, 2, 3, 1, 2, 3, 4}, // 9
    {3, 2, 3, 4, 2, 1, 2, 3, 1, 0, 1, 2, 2, 1, 2, 3}, // 10
    {4, 3, 2, 3, 3, 2, 1, 2, 2, 1, 0, 1, 3, 2, 1, 2}, // 11
    {5, 4, 3, 2, 4, 3, 2, 1, 3, 2, 1, 0, 4, 3, 2, 1}, // 12
    {3, 4, 5, 6, 2, 3, 4, 5, 1, 2, 3, 4, 0, 1, 2, 3}, // 13
    {4, 3, 4, 5, 3, 2, 3, 4, 2, 1, 2, 3, 1, 0, 1, 2}, // 14
    {5, 4, 3, 4, 4, 3, 2, 3, 3, 3, 1, 2, 2, 1, 0, 1}, // 15
};

// パネルの並びの情報を持つクラス
class Board {
public:
  // パネルの並びを保持する配列
  int panel[PANEL_NUM];
  // ゼロのパネルの位置
  int zeroPos;
  // ゴールに到達するまでに必要なコスト
  int cost;
  // 並び替えた回数
  int moveCount;

  // コンストラクタ
  Board(int board[], int mCount) {
    for (int i = 0; i < PANEL_NUM; i++) {
      panel[i] = board[i];
    }

    zeroPos = searchZeroPos();
    cost = calcMoveDistance();

    moveCount = mCount;
  }

  // 演算子のオーバーロード ( sort()で使用 )
  bool operator<(const Board &e) const {
    // costをキーにして並べ替える
    return cost < e.cost;
  }

  // 配列中のゼロの位置を探索する関数
  int searchZeroPos() {
    for (int i = 0; i < PANEL_NUM; i++) {
      if (panel[i] == 0) {
        return i;
      }
    }

    return -1;
  }

  // ゴールに到達したかを判定する関数
  bool reachGoal() {
    for (int i = 0; i < PANEL_NUM; i++) {
      if (panel[i] != ary_Target[i]) {
        return false;
      }
    }

    return true;
  }

  // パネルの並びから, 正解までに必要な移動距離を求める
  int calcMoveDistance() {
    int num = 0;
    int distance = 0;

    for (int i = 0; i < PANEL_NUM; i++) {
      num = panel[i];

      // 最短の移動距離の和を求める
      distance += moveDistance[num][i];
    }

    return distance;
  }

  // パネルの配列の要素の比較を実施する
  bool panelCompare(int compPanel[]) {
    for (int i = 0; i < PANEL_NUM; i++) {
      if (panel[i] != compPanel[i]) {
        return false;
      }
    }

    return true;
  }
};

// 移動したパネルの状態を保持するvector
vector<Board> v_board;

// 検索したパネルを保持するvector
vector<Board> v_history;

// 最小移動回数を保持する変数
int minMoveCount;

void panelMoveOperation();
int calcArrayNum(int x, int y);

// メイン関数
int main(void) {
  int ary_panel[PANEL_NUM];

  minMoveCount = 0;

  // 初期状態を設定する
  for (int i = 0; i < PANEL_NUM; i++) {
    cin >> ary_panel[i];
  }

  // 初期設定
  Board Bad = Board(ary_panel, 0);

  v_board.push_back(Bad);
  v_history.push_back(Bad);

  panelMoveOperation();

  // 結果の表示
  cout << minMoveCount << endl;

  return 0;
}

// x, y 座標から配列の要素数へ変換する関数
int calcArrayNum(int x, int y) { return 4 * x + y; }

// パネルの移動操作を実施する関数
void panelMoveOperation(void) {
  // 要素の並び替えを実施する
  sort(v_board.begin(), v_board.end());

  // コストの一番小さい, 先頭要素を取り出す
  Board Bad = Board(v_board[0].panel, v_board[0].moveCount);
  // 全要素を削除する
  v_board.erase(v_board.begin(), v_board.end());

  // 並び替えた状態がゴールに到達しているかを確認する
  if (Bad.reachGoal() == true) {
    // 検索終了
    minMoveCount = Bad.moveCount;
    return;
  }

  int x, y;

  // ゼロの位置を x, y 座標に変換する
  x = Bad.zeroPos / 4;
  y = Bad.zeroPos % 4;

  // 上下左右にパネルを移動させる
  for (int i = 0; i < MOVE_LAST; i++) {

    int tx, ty;
    bool searchFlag;

    // 移動後の座標を計算する
    tx = x + move_p[i].x;
    ty = y + move_p[i].y;

    if ((0 <= tx && tx <= 3) && (0 <= ty && ty <= 3)) {
      searchFlag = true;

      // 移動用の新しい実体を作成する
      Board newBoard = Board(Bad.panel, Bad.moveCount);

      int moveNum, movePos;

      // 座標から配列の要素No.へ変換する
      movePos = calcArrayNum(tx, ty);
      // 移動するパネルNo.を取得する
      moveNum = newBoard.panel[movePos];

      // パネルを入れ替える
      swap(newBoard.panel[Bad.zeroPos], newBoard.panel[movePos]);
      newBoard.moveCount++;

      for (int num = 0; num < v_history.size(); num++) {
        if (v_history[num].panelCompare(newBoard.panel) == true) {
          searchFlag = false;
          break;
        }
      }

      if (searchFlag == true) {
        // コストの再計算を実施する
        newBoard.cost = newBoard.cost - moveDistance[moveNum][movePos] +
                        moveDistance[moveNum][newBoard.zeroPos];

        // 新しい盤面を vector に追加する
        v_board.push_back(newBoard);
        v_history.push_back(newBoard);
      }
    }
  }

  // 再帰呼び出しを実施
  panelMoveOperation();

  return;
}