Problem p02246 (C++) — buggy submission, verdict: Time Limit Exceeded

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

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

using namespace std;

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 };

// 各パネルを正解の位置まで動かすのに必要な最短距離
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
};

// パネルの状態を保持する配列
int ary_panel[PANEL_NUM];

// 動かしたパネルを記憶しておく配列
int ary_movedPanel[MAX_STEP];

// 検索中かを保持するフラグ
// true : 検索中, false : 検索終了
bool searchState;

void panelMoveOperation(int stepLimit, int moveCount, int zeroPos,
                        int lowerCount);
int calcArrayNum(int x, int y);
int searchZeroPos(int panel[]);
bool reachGoal(int panel[]);
int calcMoveDistance(int panel[]);

// メイン関数
int main(void) {
  int lowerMoveCount = 0;

  searchState = true;

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

  // 初期設定
  ary_movedPanel[0] = 0;

  // 正解に到達するまでの最短手数を求めておく
  lowerMoveCount = calcMoveDistance(&ary_panel[0]);

  for (int stepLimit = 0; stepLimit <= MAX_STEP; stepLimit++) {
    int zeroPos = searchZeroPos(&ary_panel[0]);

    // 検索可能な手数を順番に増やして検索する
    panelMoveOperation(stepLimit, 0, zeroPos, lowerMoveCount);

    if (searchState == false) {
      // 検索終了
      printf("%d\\n", stepLimit);
      break;
    }
  }

  return 0;
}

// パネルの移動操作を実施する関数
// stepLimit  : 検索可能な手数の上限値
// moveCount  : パネルを移動させた回数
// zeroPos    : 配列中のパネルのゼロの位置
// lowerCount : 正解までに必要な最短の移動距離
void panelMoveOperation(int stepLimit, int moveCount, int zeroPos,
                        int lowerCount) {
  int newLowerCount = 0;

  if (stepLimit == moveCount) {

    // 並び替えた状態がゴールに到達しているかを確認する
    if (reachGoal(&ary_panel[0]) == true) {
      // 検索終了
      searchState = false;
    }

    return;
  }

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

    // zeroPos = searchZeroPos();

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

    int tx, ty;

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

    if ((0 <= tx && tx <= 3) && (0 <= ty && ty <= 3)) {
      int moveNum, movePos;

      // 座標から配列の要素No.へ変換する
      movePos = calcArrayNum(tx, ty);
      moveNum = ary_panel[movePos];

      if (moveNum == ary_movedPanel[moveCount]) {
        // 前回動かしたパネルなら, 元に戻るだけなので何もしない
        continue;
      }

      // 動かしたパネルを記憶しておく
      ary_movedPanel[moveCount + 1] = moveNum;

      // 数値を入れ替える
      swap(ary_panel[zeroPos], ary_panel[movePos]);

      // 正解までに必要な移動の最短距離を再計算する
      // [現在の最短距離] - [移動前のパネル位置での正解までの距離] +
      // [移動後のパネル位置での正解までの距離] パネルを移動させるので,
      // 移動前の距離は引いておいて, 新たに移動後の距離を足しておく
      newLowerCount = lowerCount - moveDistance[moveNum][movePos] +
                      moveDistance[moveNum][zeroPos];

      // 下限値枝刈り法の実施
      if ((moveCount + newLowerCount) <= stepLimit) {
        // 再帰呼び出しを実施
        panelMoveOperation(stepLimit, moveCount + 1, movePos, newLowerCount);
      }

      // 再帰呼び出しの前の状態に戻す
      ary_panel[zeroPos] = 0;
      ary_panel[movePos] = moveNum;
    }
  }

  return;
}

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

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

  return -1;
}

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

  return true;
}

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

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

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

  return distance;
}


Error: TLE

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;

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};

// パネルの状態を保持する配列
int ary_panel[PANEL_NUM];

// 動かしたパネルを記憶しておく配列
int ary_movedPanel[MAX_STEP];

// 検索中かを保持するフラグ
// true : 検索中, false : 検索終了
bool searchState;

void panelMoveOperation(int stepLimit, int moveCount, int zeroPos);
int calcArrayNum(int x, int y);
int searchZeroPos(void);
bool reachGoal(void);

// メイン関数
int main(void) {
  searchState = true;

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

  // 初期設定
  ary_movedPanel[0] = 0;

  for (int stepLimit = 0; stepLimit <= MAX_STEP; stepLimit++) {
    int zeroPos = searchZeroPos();

    // 検索可能な手数を順番に増やしていく
    panelMoveOperation(stepLimit, 0, zeroPos);

    if (searchState == false) {
      // 検索終了
      printf("%d\\n", stepLimit);
      break;
    }
  }

  return 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 };

// パネルの移動操作を実施する関数
// zeroPos : 配列中のパネルのゼロの位置
void panelMoveOperation(int stepLimit, int moveCount, int zeroPos) {
  if (stepLimit == moveCount) {

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

    return;
  }

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

    // zeroPos = searchZeroPos();

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

    int tx, ty;

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

    if ((0 <= tx && tx <= 3) && (0 <= ty && ty <= 3)) {
      int moveNum, movePos;

      // 座標から配列の要素No.へ変換する
      movePos = calcArrayNum(tx, ty);
      moveNum = ary_panel[movePos];

      if (moveNum == ary_movedPanel[moveCount]) {
        // 前回動かしたパネルなら, 元に戻るだけなので何もしない
        continue;
      }

      // 動かしたパネルを記憶しておく
      ary_movedPanel[moveCount + 1] = moveNum;

      // 数値を入れ替える
      swap(ary_panel[zeroPos], ary_panel[movePos]);

      // 再帰呼び出しを実施
      panelMoveOperation(stepLimit, moveCount + 1, movePos);

      // 再帰呼び出しの前の状態に戻す
      ary_panel[zeroPos] = 0;
      ary_panel[movePos] = moveNum;
    }
  }

  return;
}

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

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

  return -1;
}

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

  return true;
}