Problem:
15 Puzzle
The goal of the 15 puzzle problem is to complete pieces on $4 \times 4$ 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 15 as shown below.
1 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15
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
9 10 11 12
13 14 15 0
Write a program which reads an initial state of the puzzle and prints the fewest steps to solve the puzzle.
Input
The $4 \times 4$ integers denoting the pieces or space are given.
Output
Print the fewest steps in a line.
Constraints
The given puzzle is solvable in at most 45 steps.
Sample Input
1 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15
Sample Output
8

Submitted solutions:

code_snippet: //迭代加深A*/IDA*
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
using namespace std;
#define N 4
#define N2 16
const int LIMIT=100;
struct puzzle{
    int f[N2];
    int space;
    int MD;
};
int MDT[N2][N2];
int limit;
puzzle state;
int path[LIMIT];
int initial(puzzle p){
    int sum=0;
    for(int i=0;i<N2;i++){
        if(p.f[i]==N2) continue;
        sum+=abs(i/N-(p.f[i]-1)/N)+abs(i%N-(p.f[i]-1)%N);
    }
    return sum;
}
const int dx[4]={-1,0,1,0};
const int dy[4]={0,-1,0,1};
char dir[4]={'u','l','d','r'};
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 tem;
    for(int r=0;r<4;r++){
        if(max(prev,r)-min(prev,r)==2) continue;
        int tx=sx+dx[r];
        int ty=sy+dy[r];
        if(tx<0||ty<0||tx>=N||ty>=N) continue;
        tem=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[state.space],state.f[tx*N+ty]);
        state.space=tx*N+ty;
        if(dfs(depth+1,r)){path[depth]=r; return true;}
        state=tem;
    }
    return false;
}
string deepening(puzzle in){
    state=in;
    state.MD=initial(in);
    for(limit=state.MD;limit<=LIMIT;limit++){
        if(dfs(0,-100)){
            string ans;
            ans="";
            for(int i=0;i<limit;i++){
                ans+=dir[path[i]];
            }
            return ans;
        }
    }
    return "unsolvable";
}
int main(){
    puzzle in;
    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);
        }
    }
    for(int i=0;i<N2;i++){
        cin>>in.f[i];
        if(in.f[i]==0){
            in.f[i]=N2;
            in.space=i;
        }
    }
    string answer=deepening(in);
    cout<<(int)answer.size()<<endl;
    return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 3440
final_rank: 3

code_snippet: #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
using namespace std;

static const int N = 4;
static const int N2 = 16;
static const int 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];
    int space;
    int 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 "unsolvalbe";
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    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;
}
accuracy: 1
time_ms: 60
memory_kb: 3444
final_rank: 4

code_snippet: // A*
#include <iostream>
#include <cmath>
#include <map>
#include <queue>

constexpr int N = 4;
constexpr int N2 = 16;

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];
    int space;
    int MD;
    int cost;

    bool operator<(const Puzzle& p) const {
        for  (int i = 0; i < N2; ++i) {
            if (f[i] == p.f[i]) {
                continue;
            }
            return f[i] < p.f[i];
        }
        return false;
    }
};

struct State {
    Puzzle puzzle;
    int estimated;
    bool operator<(const State& s) const {
        return estimated > s.estimated;
    }
};

int get_all_MD(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;
}

int astar(Puzzle s) {
    std::priority_queue<State> PQ;
    s.MD = get_all_MD(s);
    s.cost = 0;
    std::map<Puzzle, bool> V;
    Puzzle u, v;
    State initial;
    initial.puzzle = s;
    initial.estimated = get_all_MD(s);
    PQ.push(initial);

    while (!PQ.empty()) {
        State st = PQ.top();
        PQ.pop();
        u = st.puzzle;

        if (u.MD == 0) {
            return u.cost;
        }
        V[u] = true;

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

        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;
            }
            v = u;

            v.MD -= MDT[tx * N + ty][v.f[tx * N + ty] - 1];
            v.MD += MDT[sx * N + sy][v.f[tx * N + ty] - 1];

            std::swap(v.f[sx * N + sy], v.f[tx * N + ty]);
            v.space = tx * N + ty;
            if (!V[v]) {
                ++v.cost;
                State news;
                news.puzzle = v;
                news.estimated = v.cost + v.MD;
                PQ.push(news);
            }
        }
    }
    return -1;
}

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

    Puzzle in;

    for (int i = 0; i < N2; ++i) {
        std::cin >> in.f[i];
        if (in.f[i] == 0) {
            in.f[i] = N2;
            in.space = i;
        }
    }
    std::cout << astar(in) << std::endl;

    return 0;
}
accuracy: 1
time_ms: 1900
memory_kb: 259284
final_rank: 16

code_snippet: // IDA* (Iterative Deepening)
#include <iostream>
#include <cmath>
#include <string>

constexpr int N = 4;
constexpr int N2 = 16;
constexpr int 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];
    int space;
    int MD;
};

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

int get_all_MD(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 is_solved() {
    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 (std::max(prev, r) - std::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];
        std::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;
}

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

    for (limit = in.MD; limit <= LIMIT; ++limit) {
        state = in;
        if (dfs(0, -100)) {
            std::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] = std::abs(i / N - j / N) + std::abs(i % N - j % N);
        }
    }

    Puzzle in;

    for (int i = 0; i < N2; ++i) {
        std::cin >> in.f[i];
        if (in.f[i] == 0) {
            in.f[i] = N2;
            in.space = i;
        }
    }
    std::string ans = iterative_deepening(in);
    std::cout << ans.size() << std::endl;

    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 3584
final_rank: 2

code_snippet: #include <stdio.h>
#include <stdlib.h>

#define N 4
#define N2 16
#define LIMIT 45 // 問題の制約

int puzzle[N2];
int blank_pos;
int path[LIMIT];

// ゴール状態の各タイルの座標 (マンハッタン距離計算用)
int goal_x[] = {3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2};
int goal_y[] = {3, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3};

// 移動方向 (上、下、左、右)
int dx[] = {0, 0, -1, 1};
int dy[] = {-1, 1, 0, 0};

// マンハッタン距離を計算するヒューリスティック関数
int heuristic() {
    int dist = 0;
    for (int i = 0; i < N2; i++) {
        if (puzzle[i] == 0) continue;
        int val = puzzle[i];
        int current_y = i / N;
        int current_x = i % N;
        dist += abs(current_y - goal_y[val]) + abs(current_x - goal_x[val]);
    }
    return dist;
}

// 反復深化A*の再帰的な探索部分
int ida_star(int depth, int prev_move, int threshold) {
    int h = heuristic();
    
    // 現在のコスト(depth) + 推定コスト(h) がしきい値を超えたら枝刈り
    if (depth + h > threshold) {
        return depth + h;
    }
    // ゴールに到達
    if (h == 0) {
        return 0; // 成功
    }

    int min_threshold = 1000; // 次のしきい値候補

    int by = blank_pos / N;
    int bx = blank_pos % N;
    
    for (int i = 0; i < 4; i++) {
        // 直前の手と逆の動きはしない (例: 上に動かした直後に下には動かさない)
        if (prev_move >= 0 && prev_move + i == 1) continue; // 0:上, 1:下
        if (prev_move >= 2 && prev_move + i == 5) continue; // 2:左, 3:右

        int ny = by + dy[i];
        int nx = bx + dx[i];

        if (ny >= 0 && ny < N && nx >= 0 && nx < N) {
            int next_blank_pos = ny * N + nx;

            // パネルを交換
            puzzle[blank_pos] = puzzle[next_blank_pos];
            puzzle[next_blank_pos] = 0;
            blank_pos = next_blank_pos;
            
            int new_threshold = ida_star(depth + 1, i, threshold);
            
            // 盤面を元に戻す
            blank_pos = by * N + bx;
            puzzle[next_blank_pos] = puzzle[blank_pos];
            puzzle[blank_pos] = 0;

            if (new_threshold == 0) return 0; // ゴールが見つかった
            if (new_threshold < min_threshold) {
                min_threshold = new_threshold;
            }
        }
    }

    return min_threshold;
}

int main() {
    for (int i = 0; i < N2; i++) {
        scanf("%d", &puzzle[i]);
        if (puzzle[i] == 0) {
            blank_pos = i;
        }
    }

    // 最初のしきい値は初期状態のヒューリスティック値
    int threshold = heuristic();

    while (1) {
        int next_threshold = ida_star(0, -1, threshold);
        if (next_threshold == 0) {
            printf("%d\n", threshold);
            break;
        }
        if (next_threshold > LIMIT) {
            // 制約上、45手以内で解けるはず
            break;
        }
        threshold = next_threshold;
    }

    return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 2984
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (i = 0; i < (int)(n); i++)
using ll = long long;

static const int N = 4;
static const vector<int> dx = {-1, 0, 1, 0};
static const vector<int> dy = {0, -1, 0, 1};
static const vector<char> dir = {'u', 'l', 'd', 'r'};
static const int LIMIT = 45;
vector<vector<int>> MDT(N *N, vector<int>(N *N));

struct Puzzle
{
    vector<vector<int>> f = vector<vector<int>>(N, vector<int>(N));
    int sx;
    int sy;
    int MD;

    bool operator<(const Puzzle &p) const
    {
        int i;
        int j;
        rep(i, N)
        {
            rep(j, N)
            {
                if (f.at(i).at(j) == p.f.at(i).at(j))
                {
                    continue;
                }
                return f.at(i).at(j) > p.f.at(i).at(j);
            }
        }
        return false;
    }
};

void makeMDT()
{
    int i;
    int j;

    rep(i, N * N)
    {
        rep(j, N * N)
        {
            MDT.at(i).at(j) = abs(i / N - j / N) + abs(i % N - j % N);
        }
    }
    return;
}

int getAllMD(Puzzle pz)
{
    int sum;
    int i;
    int j;
    int tgt;

    sum = 0;
    rep(i, N)
    {
        rep(j, N)
        {
            tgt = (i * N + j) + 1;
            if (pz.f.at(i).at(j) == tgt || pz.f.at(i).at(j) == N * N)
            {
                continue;
            }
            sum += MDT.at(tgt - 1).at(pz.f.at(i).at(j) - 1);
        }
    }
    return sum;
}

bool isSolved(Puzzle s)
{
    int i;
    int j;

    rep(i, N)
    {
        rep(j, N)
        {
            if (s.f.at(i).at(j) != i * N + j + 1)
            {
                return false;
            }
        }
    }
    return true;
}

bool dfs(Puzzle &state, int limit, int depth, int prev, vector<int> &path)
{
    Puzzle tmp;
    int r;
    int tx;
    int ty;

    if (state.MD == 0)
    {
        return true;
    }
    if (depth + state.MD > limit)
    {
        return false;
    }

    rep(r, 4)
    {
        tx = state.sx + dx.at(r);
        ty = state.sy + dy.at(r);
        if (tx < 0 || ty < 0 || tx >= N || ty >= N)
        {
            continue;
        }
        if (abs(prev - r) == 2)
        {
            continue;
        }
        tmp = state;
        state.MD -= MDT.at(tx * N + ty).at(state.f.at(tx).at(ty) - 1);
        state.MD += MDT.at(state.sx * N + state.sy).at(state.f.at(tx).at(ty) - 1);
        swap(state.f.at(tx).at(ty), state.f.at(state.sx).at(state.sy));
        state.sx = tx;
        state.sy = ty;
        if (dfs(state, limit, depth + 1, r, path))
        {
            path.at(depth) = r;
            return true;
        }
        state = tmp;
    }
    return false;
}

string iterative_deepening(Puzzle in)
{
    int limit;
    int i;
    Puzzle state;
    string ans;

    in.MD = getAllMD(in);
    for (limit = in.MD; limit <= LIMIT; limit++)
    {
        vector<int> path(limit);
        state = in;
        if (dfs(state, limit, 0, -100, path))
        {
            ans = "";
            rep(i, limit)
            {
                ans += dir.at(path.at(i));
            }
            return ans;
        }
    }
    return "";
}

int main()
{
    int i;
    int j;
    Puzzle in;
    string ans;

    makeMDT();

    rep(i, N)
    {
        rep(j, N)
        {
            cin >> in.f.at(i).at(j);
            if (in.f.at(i).at(j) == 0)
            {
                in.f.at(i).at(j) = N * N;
                in.sx = i;
                in.sy = j;
            }
        }
    }

    ans = iterative_deepening(in);
    cout << ans.size() << endl;
    return 0;
}
accuracy: 1
time_ms: 750
memory_kb: 3584
final_rank: 13

code_snippet: #include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>
#include <string>
#include <algorithm>

using namespace std;

// 目标状态
const string target = "123456789ABCDEF0";

// 计算曼哈顿距离
int manhattanDistance(const string& state) {
    int dist = 0;
    for (int i = 0; i < 16; ++i) {
        if (state[i] == '0') continue;
        int targetPos = state[i] - '1';
        int currentRow = i / 4;
        int currentCol = i % 4;
        int targetRow = targetPos / 4;
        int targetCol = targetPos % 4;
        dist += abs(currentRow - targetRow) + abs(currentCol - targetCol);
    }
    return dist;
}

// 找到空格（数字 0）的位置
int findZero(const string& state) {
    return state.find('0');
}

// 交换字符串中两个位置的字符
string swapChars(string state, int i, int j) {
    swap(state[i], state[j]);
    return state;
}

// 节点结构体
struct Node {
    string state;
    int g;  // 从起始节点到当前节点的实际代价
    int h;  // 从当前节点到目标节点的估计代价
    int f;  // f = g + h

    Node(string s, int _g) : state(s), g(_g) {
        h = manhattanDistance(state);
        f = g + h;
    }

    // 重载小于运算符，用于优先队列
    bool operator>(const Node& other) const {
        return f > other.f;
    }
};

// A* 算法
int aStar(const string& start) {
    priority_queue<Node, vector<Node>, greater<Node>> pq;
    unordered_set<string> visited;

    pq.push(Node(start, 0));
    visited.insert(start);

    while (!pq.empty()) {
        Node current = pq.top();
        pq.pop();

        if (current.state == target) {
            return current.g;
        }

        int zeroPos = findZero(current.state);
        int row = zeroPos / 4;
        int col = zeroPos % 4;

        // 尝试四个方向的移动
        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};

        for (int i = 0; i < 4; ++i) {
            int newRow = row + dx[i];
            int newCol = col + dy[i];

            if (newRow >= 0 && newRow < 4 && newCol >= 0 && newCol < 4) {
                int newPos = newRow * 4 + newCol;
                string nextState = swapChars(current.state, zeroPos, newPos);

                if (visited.find(nextState) == visited.end()) {
                    pq.push(Node(nextState, current.g + 1));
                    visited.insert(nextState);
                }
            }
        }
    }

    return -1; // 理论上不会执行到这里，因为题目保证有解
}

int main() {
    string start;
    for (int i = 0; i < 16; ++i) {
        int num;
        cin >> num;
        if (num == 0) {
            start += '0';
        } else if (num < 10) {
            start += to_string(num);
        } else {
            start += 'A' + num - 10;
        }
    }

    int result = aStar(start);
    cout << result << endl;

    return 0;
}
accuracy: 0.6428571428571429
time_ms: 520
memory_kb: 92756
final_rank: 20

code_snippet: #include <iostream>
#include <queue>
#include <unordered_map>
#include <cmath>
#include <string>
#include <vector>

using namespace std;

const int n = 4;
const int maxLimit = 45;
const vector<int> dr = {0, 1, 0, -1};
const vector<int> dc = {1, 0, -1, 0};

struct Puzzle {
    string board;
    int zeroPos;
    int g;

    Puzzle(string b, int zp, int depth) : board(b), zeroPos(zp), g(depth) {}

    int getHeuristic() const {
        int h = 0;
        for (int i = 0; i < 16; ++i) {
            if (board[i] == '0') continue;
            int val = board[i] - '1';
            int goalRow = val / 4, goalCol = val % 4;
            int curRow = i / 4, curCol = i % 4;
            h += abs(goalRow - curRow) + abs(goalCol - curCol);
        }
        return h;
    }

    bool isGoal() const {
        for (int i = 0; i < 15; ++i)
            if (board[i] != '1' + i) return false;
        return board[15] == '0';
    }
};

// Custom comparator for priority_queue
struct ComparePuzzle {
    bool operator()(const pair<int, Puzzle>& a, const pair<int, Puzzle>& b) const {
        return a.first > b.first;  // min-heap by f = g + h
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    string inputBoard = "";
    int zeroPos = 0;

    for (int i = 0, num; i < 16; ++i) {
        cin >> num;
        inputBoard += static_cast<char>('0' + num);
        if (num == 0) zeroPos = i;
    }

    Puzzle start(inputBoard, zeroPos, 0);
    priority_queue<pair<int, Puzzle>, vector<pair<int, Puzzle>>, ComparePuzzle> pq;

    unordered_map<string, int> visited;
    int h = start.getHeuristic();
    pq.emplace(h, start);
    visited[start.board] = 0;

    while (!pq.empty()) {
        auto [f, cur] = pq.top();
        pq.pop();

        if (cur.isGoal()) {
            cout << cur.g << '\n';
            return 0;
        }

        if (cur.g > maxLimit) continue;

        int r = cur.zeroPos / 4, c = cur.zeroPos % 4;

        for (int i = 0; i < 4; ++i) {
            int nr = r + dr[i], nc = c + dc[i];
            if (nr < 0 || nr >= 4 || nc < 0 || nc >= 4) continue;

            int nextPos = nr * 4 + nc;
            string nextBoard = cur.board;
            swap(nextBoard[cur.zeroPos], nextBoard[nextPos]);

            int newG = cur.g + 1;
            if (visited.count(nextBoard) && visited[nextBoard] <= newG) continue;

            visited[nextBoard] = newG;
            Puzzle next(nextBoard, nextPos, newG);
            int nextH = next.getHeuristic();
            pq.emplace(newG + nextH, next);
        }
    }

    cout << -1 << '\n';
    return 0;
}
accuracy: 1
time_ms: 1330
memory_kb: 244820
final_rank: 15

code_snippet: #include <iostream>
#include <stdio.h>
#include <string>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#include <bitset>
#include <climits>
#include <functional>
using namespace std;

int n = 4, limit = 0, maxLimit = 45;

vector<int> dr{0, 1, 0, -1}, dc{1, 0, -1, 0};

struct Puzzle {
    vector<vector<int> > mat;
    pair<int, int> zeroPos;

    Puzzle() {
        mat.resize(n, vector<int>(n));
    }

    void swapZero(pair<int, int> nextPos) {
        swap(mat[zeroPos.first][zeroPos.second], mat[nextPos.first][nextPos.second]);
        zeroPos = nextPos;
    }

    bool isGoal() {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int goal = (i * n + j + 1) % (n * n);
                if (mat[i][j] != goal) return false;
            }
        }
        return true;
    }

    int getCost() {
        int cost = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (mat[i][j] == 0) continue;
                int num = mat[i][j] - 1;
                int goali = num / n, goalj = num % n;

                cost += (abs(goali - i) + abs(goalj - j));
            }
        }

        return cost;
    }

    void print() {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cout << mat[i][j] << " ";
            }
            cout << endl;
        }
        cout << "--------" << endl;
    }
};

int dfs(Puzzle &cur, pair<int, int> lastPos, int d) {
    if (cur.isGoal()) return d;

    // if ((d == 1 || d == 2) && limit == 40) {
    //     cur.print();
    // }

    pair<int, int> curPos = cur.zeroPos;

    for (int i = 0; i < 4; i++) {
        int nextr = curPos.first + dr[i], nextc = curPos.second + dc[i];
        
        if (nextr < 0 || nextr >= n || nextc < 0 || nextc >= n) continue;

        pair<int, int> nextPos = {nextr, nextc};
        if (lastPos == nextPos) continue;

        cur.swapZero(nextPos);

        int cost = cur.getCost();
        
        if (d + cost <= limit) {
            int res = dfs(cur, curPos, d + 1);
            if (res != -1) return res;
        }

        cur.swapZero(curPos);
    }

    return -1;
}

int main () {
    Puzzle puzzle;

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            cin >> puzzle.mat[i][j];
            if (puzzle.mat[i][j] == 0) {
                puzzle.zeroPos = {i, j};
            }
        }
    }

    for (; limit <= maxLimit; limit++) {
        int res = dfs(puzzle, {-1, -1}, 0);
        if (res != -1) {
            cout << res << endl;
            return 0;
        }
    }

    cout << -1 << endl;

    return 0;
}
accuracy: 1
time_ms: 240
memory_kb: 3452
final_rank: 12

code_snippet: #include <iostream>
#include <stdio.h>
#include <string>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#include <bitset>
#include <climits>
#include <functional>
using namespace std;

int n = 4, limit = 0, maxLimit = 45;

vector<int> dr{0, 1, 0, -1}, dc{1, 0, -1, 0};

struct Puzzle {
    vector<vector<int> > mat;
    pair<int, int> zeroPos;

    Puzzle() {
        mat.resize(n, vector<int>(n));
    }

    void swapZero(pair<int, int> nextPos) {
        swap(mat[zeroPos.first][zeroPos.second], mat[nextPos.first][nextPos.second]);
        zeroPos = nextPos;
    }

    bool isGoal() {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int goal = (i * n + j + 1) % (n * n);
                if (mat[i][j] != goal) return false;
            }
        }
        return true;
    }

    int getCost() {
        int cost = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int num = (mat[i][j] + (n * n) - 1) % (n * n);

                int goali = num / n, goalj = num % n;

                cost += (abs(goali - i) + abs(goalj - j));
            }
        }

        return cost;
    }
};

int dfs(Puzzle &cur, pair<int, int> lastPos, int d) {
    if (cur.isGoal()) return d;

    pair<int, int> curPos = cur.zeroPos;

    for (int i = 0; i < 4; i++) {
        int nextr = curPos.first + dr[i], nextc = curPos.second + dc[i];
        
        if (nextr < 0 || nextr >= n || nextc < 0 || nextc >= n) continue;

        pair<int, int> nextPos = {nextr, nextc};
        if (lastPos == nextPos) continue;

        cur.swapZero(nextPos);

        int cost = cur.getCost();
        
        if (d + cost <= limit) {
            int res = dfs(cur, curPos, d + 1);
            if (res != -1) return res;
        }

        cur.swapZero(curPos);
    }

    return -1;
}

int main () {
    Puzzle puzzle;

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            cin >> puzzle.mat[i][j];
            if (puzzle.mat[i][j] == 0) {
                puzzle.zeroPos = {i, j};
            }
        }
    }

    for (; limit <= maxLimit; limit++) {
        int res = dfs(puzzle, {-1, -1}, 0);
        if (res != -1) {
            cout << res << endl;
            return 0;
        }
    }

    cout << -1 << endl;

    return 0;
}
accuracy: 0.8571428571428571
time_ms: 750
memory_kb: 3456
final_rank: 18

code_snippet: #include <iostream>
#include <stdio.h>
#include <string>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#include <bitset>
#include <climits>
#include <functional>
using namespace std;

int n = 4, limit = 0, maxLimit = 45;

vector<int> dr{0, 1, 0, -1}, dc{1, 0, -1, 0};

struct Puzzle {
    vector<vector<int> > mat;
    pair<int, int> zeroPos;

    Puzzle() {
        mat.resize(n, vector<int>(n));
    }

    void swapZero(pair<int, int> nextPos) {
        swap(mat[zeroPos.first][zeroPos.second], mat[nextPos.first][nextPos.second]);
        zeroPos = nextPos;
    }

    bool isGoal() {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int goal = (i * n + j + 1) % (n * n);
                if (mat[i][j] != goal) return false;
            }
        }
        return true;
    }

    int getCost() {
        int cost = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int num = mat[i][j] - 1;

                int goali = num / n, goalj = num % n;

                cost += (abs(goali - i) + abs(goalj - j));
            }
        }

        return cost;
    }
};

int dfs(Puzzle &cur, pair<int, int> lastPos, int d) {
    if (cur.isGoal()) return d;

    pair<int, int> curPos = cur.zeroPos;

    for (int i = 0; i < 4; i++) {
        int nextr = curPos.first + dr[i], nextc = curPos.second + dc[i];
        
        if (nextr < 0 || nextr >= n || nextc < 0 || nextc >= n) continue;

        pair<int, int> nextPos = {nextr, nextc};
        if (lastPos == nextPos) continue;

        cur.swapZero(nextPos);

        int cost = cur.getCost();
        
        if (d + cost <= limit) {
            int res = dfs(cur, curPos, d + 1);
            if (res != -1) return res;
        }

        cur.swapZero(curPos);
    }

    return -1;
}

int main () {
    Puzzle puzzle;

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            cin >> puzzle.mat[i][j];
            if (puzzle.mat[i][j] == 0) {
                puzzle.zeroPos = {i, j};
            }
        }
    }

    for (; limit <= maxLimit; limit++) {
        int res = dfs(puzzle, {-1, -1}, 0);
        if (res != -1) {
            cout << res << endl;
            return 0;
        }
    }

    cout << -1 << endl;

    return 0;
}
accuracy: 0.7142857142857143
time_ms: 1300
memory_kb: 3464
final_rank: 19

code_snippet: #include<cstdio>
#include<iostream>
#include<cmath>
#include<map>
#include<queue>

using namespace std;
#define N 4
#define N2 16

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;
  int cost;

  bool operator < ( const Puzzle &p ) const {
    for ( int i = 0; i < N2; i++ ) {
      if ( f[i] == p.f[i] ) continue;
      return f[i] < p.f[i];
    }
    return false;
  }
};
struct State {
  Puzzle puzzle;
  int estimated;
  bool operator < (const State &s) const {
    return estimated > s.estimated;
  }
};

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

int astar(Puzzle s) {
  priority_queue<State> PQ;
  s.MD = getAllMD(s);
  s.cost = 0;
  map<Puzzle, bool> V;
  Puzzle u, v;
  State initial;
  initial.puzzle = s;
  initial.estimated = getAllMD(s);
  PQ.push(initial);
  while ( !PQ.empty() ) {
    State st = PQ.top(); PQ.pop();
    u = st.puzzle;

    if ( u.MD == 0 ) return u.cost;
    V[u] = true;

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

    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;
      v = u;

      v.MD -= MDT[tx * N + ty][v.f[tx * N + ty] - 1];
      v.MD += MDT[sx * N + sy][v.f[tx * N + ty] - 1];

      swap(v.f[sx * N + sy], v.f[tx * N + ty]);
      v.space = tx * N + ty;
      if ( !V[v] ) {
        v.cost++;
        State news;
        news.puzzle = v;
        news.estimated = v.cost + v.MD;
        PQ.push(news);
      }
    }
  }
  return -1;
}

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;
    }
  }
  cout << astar(in) << endl;

  return 0;
}
accuracy: 1
time_ms: 1980
memory_kb: 258956
final_rank: 17

code_snippet: #include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cmath>
#include <queue>
#include <map>
using namespace std;

#define MAX_SIZE (4)

int b[MAX_SIZE][MAX_SIZE];
int limit;

int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};

int dist() {
    int res = 0, n;
    for(int i = 0; i < MAX_SIZE; i++) {
        for(int j = 0; j < MAX_SIZE; j++) {
            if(b[i][j] == 0) continue;
            n = b[i][j]-1;
            res += abs(n/4-i) + abs(n%4-j);
        }
    }
    return res;
}

bool check(int depth, int prev, int x, int y) {
    int h = dist();
    if(h+depth > limit) return false;
    if(h == 0) return true; //目標の配置ならば終了
    for(int i = 0; i<4; i++) {
        if(abs(i-prev) == 2) continue;
        int ny = y + dy[i]; //新しい空白の座標
        int nx = x + dx[i];
        if(ny < 0 || nx < 0) continue; //新しい座標が盤面外ならスキップ
        if(ny > 3 || nx > 3) continue;
        swap(b[ny][nx], b[y][x]);
        if(check(depth+1, i, nx, ny)) return true;
        swap(b[ny][nx], b[y][x]);
    }
    return false;
}


int solve(int x, int y) {
    for(limit = 0;; limit++) {
        if(check(0, 99, x, y)) {
            cout << limit << endl;
            return 0;
        }
    }
}


int main(void) {
    int x;
    int y;
    //入力
    for(int i = 0; i < MAX_SIZE; i++) {
        for(int j = 0; j < MAX_SIZE; j++) {
            cin >> b[i][j];
            if(b[i][j] == 0) { //0の場所を記録
                x = j;
                y = i;
            }
        }
    }

    solve(x, y);

    return 0;
}
accuracy: 1
time_ms: 220
memory_kb: 3440
final_rank: 11

code_snippet: #include<bits/stdc++.h>
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,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;
}
accuracy: 1
time_ms: 60
memory_kb: 3488
final_rank: 7

code_snippet: #include <stdio.h>
#include <stdlib.h>
#define N 4
#define true 1
#define false 0

int dx[4] = {0, -1, 0, 1};
int dy[4] = {1, 0, -1, 0};
int limit;
int t[N][N];

void swap(int *x, int *y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

int getHeuristic() {
    int i, j, sum = 0, x;
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            if (t[i][j] == 0) continue; 
            x = t[i][j] - 1;
            sum += abs(x / 4 - i) + abs(x % 4 - j);
        }
    }
    return sum;
}

int dfs(int depth, int prev, int py, int px) {
    int h = getHeuristic();
    if (h == 0) return true; 
    if (depth + h > limit) return false;

    int i;
    for (i = 0; i < 4; i++) {
        if (abs(i - prev) == 2) continue;

        int tx = px + dx[i], ty = py + dy[i];
        if (tx < 0 || ty < 0 || tx >= N || ty >= N) continue; 

        swap(&t[ty][tx], &t[py][px]); 
        if (dfs(depth + 1, i, ty, tx)) return true;
        swap(&t[ty][tx], &t[py][px]); 
    }
    return false;
}

void isSolved(int py, int px) {
    for (limit = 0;; limit++) {
        if (dfs(0, 99, py, px)) {
            printf("%d\n", limit);
            return;
        }
    }
}

int main() {
    int i, j, px, py;
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            scanf("%d", &t[i][j]);
            if (t[i][j] == 0) { 
                py = i;
                px = j;
            }
        }
    }
    isSolved(py, px);
    return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 2980
final_rank: 9

code_snippet: // Iterative Deepening C++
#include<stdio.h>
#include<iostream>
#include<cmath>
#include<string>
#include<cassert>

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 fucking_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 fucking_isSolved(){
  for(int i=0;i<N2;i++) if(state.f[i]!=i+1) return false;

  return true;
}

bool fucking_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(fucking_dfs(depth+1,r)){path[depth]=r; return true;}

    state=tmp;
  }
  return false;
}


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

  for(limit=in.MD;limit<=LIMIT;limit++){
    state=in;
    if(fucking_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 = fucking_iterative_deepening(in);
  cout<<ans.size()<<endl;
  return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 3444
final_rank: 4

code_snippet: #include<bits/stdc++.h>
using namespace std;

int x[4]={-1,0,1,0};
int y[4]={0,-1,0,1};
char z[4]={'u','l','d','r'};
vector<vector<int>>mdt(16,vector<int>(16));

struct pazuru{int a[16],kuhaku,md;};
pazuru genzai;
int genkai;
vector<int>miti(101);



bool seigo(){
    for(int i=0;i<16;i++){
        if(genzai.a[i]!=i+1)return false;

        return true;
    }

}

int mdzenbu(pazuru g){
    int sum=0;
    for(int i=0;i<16;i++){
        if(g.a[i]==16)continue;
        sum+=mdt[i][g.a[i]-1];
    }
    return sum;


}

bool fukasayusen(int fukasa,int mae){
    if(genzai.md==0)return true;
    if(fukasa+genzai.md>genkai){
        return false;
    }

    int lx=genzai.kuhaku/4,ly=genzai.kuhaku%4;
    pazuru itizi;

    for(int i=0;i<4;i++){
        int mx=lx+x[i];int my=ly+y[i];
        if(mx<0 or my<0 or mx>=4 or my>=4)continue;
        if(max(mae,i)-min(mae,i)==2)continue;

        itizi=genzai;
        genzai.md-=mdt[mx*4+my][genzai.a[mx*4+my]-1];
        genzai.md+=mdt[lx*4+ly][genzai.a[mx*4+my]-1];

        swap(genzai.a[mx*4+my],genzai.a[lx*4+ly]);
        genzai.kuhaku=mx*4+my;

        if(fukasayusen(fukasa+1,i)){
            miti[fukasa]=i;
            return true;
        }
        genzai=itizi;
    }
    return false;

}

string hanpukusinka(pazuru pan){
    pan.md=mdzenbu(pan);
    for(genkai=pan.md;genkai<=101;genkai++){
        genzai=pan;
        
        if(fukasayusen(0,-100)){
            string kotae="";
            for(int i=0;i<genkai;i++){
                kotae+=z[miti[i]];
            }
            return kotae;
        }
    }
    return "unsolvable";
}

int main(){
    for(int i=0;i<16;i++){
        for(int j=0;j<16;j++){
            mdt[i][j]=abs(i/4-j/4)+abs(i%4-j%4);
        }
    }
    pazuru pan;

    for(int i=0;i<16;i++){
        cin >> pan.a[i];
        if(pan.a[i]==0){
            pan.a[i]=16;
            pan.kuhaku=i;
        }
    }
    string ans=hanpukusinka(pan);
    cout << ans.size() << endl;
    return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 3444
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;

// ゴール状態(配列で表す)
static const vector<int> GOAL = {
    1, 2, 3, 4,
    5, 6, 7, 8,
    9, 10,11,12,
    13,14,15,0
};

// 4x4 の各マスにおける(行,列)を事前に求めておく
// 例: index=0 -> (0,0), index=5 -> (1,1) など
// これによりマンハッタン距離計算が簡単になる
static vector<pair<int,int>> indexToRC(16);

// ゴール位置: タイル番号 n (1～15) が最終的にどこにあるか (row,col)
static vector<pair<int,int>> goalPos(16);

// 空白(0)が動かせる方向をプリセット
// blankPos によって動かせるパネルは異なるが、
// row, col ごとに移動可能な方向を持っていてもよい
// ここでは直接計算してもOKだが、事前に隣接先をテーブル化してもよい
int dr[4] = { -1, 1, 0, 0 };  // 上 下 左 右
int dc[4] = { 0, 0, -1, 1 };

// -------------------------------------
// ヒューリスティック: マンハッタン距離の合計
// -------------------------------------
int manhattanDist(const vector<int>& board) {
    int dist = 0;
    for(int i=0; i<16; i++){
        int val = board[i];
        if(val == 0) continue; // 空白はカウントしない
        // タイルvalの現在位置(i)とゴール位置goalPos[val]の差を足す
        auto [r, c] = indexToRC[i];
        auto [gr, gc] = goalPos[val];
        dist += abs(r - gr) + abs(c - gc);
    }
    return dist;
}

// -------------------------------------
// IDA*再帰DFS: 現在の深さg, 現在のヒューリスティックh, 閾値threshold
// をもとに探索して、解を見つければ深さ(手数)を返す。
// 見つからなければ、
//   - 次に試行すべき( g + h )の最小値(=次のthreshold候補)
// を返す。
// -------------------------------------
int idaSearch(vector<int>& board, int g, int h, int threshold, int prevBlankPos, int& nodesSearched) {
    nodesSearched++;

    int f = g + h;
    if(f > threshold) {
        // これ以上探索できないので、この先で最小となる(f= g+h)を戻す
        return f;
    }
    // goal チェック
    if(board == GOAL) {
        // 解が見つかった
        return -1; // 特殊値で「解発見」を表すことにする
    }

    // 空白(0)の位置を探す
    int blankPos = 0;
    for(int i=0; i<16; i++){
        if(board[i] == 0){
            blankPos = i;
            break;
        }
    }

    int row = blankPos / 4;
    int col = blankPos % 4;

    int minNextThreshold = INT_MAX;

    // 4方向試す (上,下,左,右)
    for(int k=0; k<4; k++){
        int nr = row + dr[k];
        int nc = col + dc[k];
        // 盤面外はスキップ
        if(nr<0 || nr>=4 || nc<0 || nc>=4) continue;

        int nextPos = nr*4 + nc;
        // 「直前の位置にただ戻るだけ」を避けたい場合
        // （prevBlankPos が -1 でなければ）
        if(prevBlankPos == nextPos) {
            // 直前に移動したタイルを元に戻す動きなのでスキップ
            continue;
        }

        // board[blankPos] と board[nextPos] を交換 (実際には0とタイルを交換)
        // 交換前のタイル番号を記録
        int tileVal = board[nextPos];

        // (ヒューリスティック更新) tileVal の位置が変わることで h がどの程度増減するか
        // 事前に計算してから入れ替えると若干効率が良い

        // 現在位置(blankPos) と nextPos の座標を取得
        auto [r0, c0] = indexToRC[blankPos];
        auto [r1, c1] = indexToRC[nextPos];
        // タイル tileVal のゴール位置 (gr, gc)
        auto [gr, gc] = goalPos[tileVal];

        // 移動前のマンハッタン距離寄与分
        // = abs(r1 - gr) + abs(c1 - gc)
        int oldCost = abs(r1 - gr) + abs(c1 - gc);
        // 移動後のマンハッタン距離寄与分
        // = abs(r0 - gr) + abs(c0 - gc)
        int newCost = abs(r0 - gr) + abs(c0 - gc);

        int deltaH = newCost - oldCost; // 増減分

        // タイル交換(実際のboard更新)
        board[blankPos] = tileVal;
        board[nextPos]  = 0;

        // 再帰探索
        int t = idaSearch(board, g + 1, h + deltaH, threshold, blankPos, nodesSearched);

        // 元に戻す
        board[nextPos]  = tileVal;
        board[blankPos] = 0;

        // t == -1 なら「解を発見した」合図
        if(t == -1) {
            return -1; 
        }
        // そうでなければ minNextThreshold を更新 (t は次に試すべき閾値候補)
        if(t < minNextThreshold) {
            minNextThreshold = t;
        }
    }

    return minNextThreshold;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // indexToRC と goalPos を初期化
    for(int i=0; i<16; i++){
        indexToRC[i] = make_pair(i/4, i%4);
    }
    // タイル n(1～15) があるべきゴール位置を先に求めておく
    // GOAL[i] = n のとき、 n が (i/4, i%4) にある
    for(int i=0; i<16; i++){
        int val = GOAL[i];
        goalPos[val] = indexToRC[i];
    }

    // パズルの入力を受け取り
    // board[0..15] に格納
    vector<int> board(16);
    for(int i=0; i<16; i++){
        cin >> board[i];
    }

    // まずヒューリスティック h を計算
    int h = manhattanDist(board);

    // IDA* の反復深化
    int threshold = h;
    const int LIMIT = 50; // 問題文により45手以内に解けるので、少し余裕持たせる

    // 深さ探索の際にノード数カウント(デバッグ用途)
    int nodesSearched = 0;

    while(threshold <= LIMIT){
        int t = idaSearch(board, 0, h, threshold, -1, nodesSearched);
        if(t == -1){
            // 解が見つかった => 閾値 threshold が 解の手数
            cout << threshold << "\n";
            return 0;
        }
        threshold = t; // 次のthresholdを更新
    }

    // ここに来ることは問題設定上ほぼ無いが、一応
    // 解が見つからなかった場合
    cout << "-1\n";
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3448
final_rank: 1

code_snippet: #include<stdio.h>
#include<stdlib.h>

int Puzzle[4][4];
int tmp[4][4];
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};

void Empty(int *, int *);
void swap(int *, int *);
int Cal();
int DFS(int, int, int, int, int);

int main(){
  int i, j, l;
  int x, y, a;

  for(i = 0 ; i < 4 ; i++){
    for(j = 0 ; j < 4 ; j++){
      scanf("%d",&Puzzle[i][j]);
    }
  }

  Empty(&x,&y);

  for(l = 0 ; l < 100 ; l++){
    for(i = 0 ; i < 4 ; i++){
      for(j = 0 ; j < 4 ; j++){
	tmp[i][j] = Puzzle[i][j];
      }
    }

    a = DFS(x, y, -1, 0, l);
    if(a != -1){
      printf("%d\n",a);
      break;
    }

    for(i = 0 ; i < 4 ; i++){
      for(j = 0 ; j < 4 ; j++){
	Puzzle[i][j] = tmp[i][j];
      }
    }
    
    a = -1;
    if(l == 99) printf("%d\n",a);
  }

  return 0;
}

void Empty(int *x, int *y){
  int i, j;

  for(i = 0 ; i < 4 ; i++){
    for(j = 0 ; j < 4 ; j++){
      if(!Puzzle[i][j]){
	*x = j;
	*y = i;
	return;
      }
    }
  }
}

void swap(int *a, int *b){
  int tmp;
  
  tmp = *a;
  *a = *b;
  *b = tmp;
}


int Cal(){
  int i, a;
  int sum = 0;

  for(i = 0 ; i < 16 ; i++){
    a = Puzzle[i/4][i%4];
    if(a){
      a--;
      sum += abs(i/4 - a/4)  + abs(i%4 - a%4);
    }
  }

  return sum;
}

int DFS(int x, int y, int p, int dep, int limit){
  int i, h, nx, ny, res;

  h = Cal();
  
  if(h == 0) return dep;
  if(dep + h > limit) return -1;

  for(i = 0 ; i < 4 ; i++){
    nx = x + dx[i];
    ny = y + dy[i];

    if(~p && i == (p+2)%4) continue;
    if(!(0 <= nx && nx < 4 && 0 <= ny && ny < 4)) continue;

    swap(&Puzzle[y][x], &Puzzle[ny][nx]);
    res = DFS(nx, ny, i, dep+1, limit);
    if(~res) return res;

    swap(&Puzzle[y][x], &Puzzle[ny][nx]);
  }

  return -1;
}
accuracy: 1
time_ms: 200
memory_kb: 2976
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <algorithm>
using namespace std;

struct Puzzle {
    vector<int> state;
    int zero_pos;
    int moves;
    int heuristic;
    
    bool operator>(const Puzzle &other) const {
        return (moves + heuristic) > (other.moves + other.heuristic);
    }
};

const vector<int> goal = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
const vector<int> dx = {-1, 1, 0, 0};
const vector<int> dy = {0, 0, -1, 1};

int manhattan_distance(const vector<int> &state) {
    int distance = 0;
    for (int i = 0; i < 16; ++i) {
        if (state[i] == 0) continue;
        int target_x = (state[i] - 1) / 4;
        int target_y = (state[i] - 1) % 4;
        int x = i / 4;
        int y = i % 4;
        distance += abs(target_x - x) + abs(target_y - y);
    }
    return distance;
}

int a_star(vector<int> start) {
    priority_queue<Puzzle, vector<Puzzle>, greater<Puzzle>> pq;
    unordered_map<string, int> visited;
    
    int zero_pos = distance(start.begin(), find(start.begin(), start.end(), 0));
    pq.push({start, zero_pos, 0, manhattan_distance(start)});
    
    while (!pq.empty()) {
        Puzzle current = pq.top(); pq.pop();
        
        if (current.state == goal) return current.moves;
        
        string state_str = "";
        for (int num : current.state) state_str += to_string(num);
        if (visited.count(state_str) && visited[state_str] <= current.moves) continue;
        visited[state_str] = current.moves;
        
        int x = current.zero_pos / 4, y = current.zero_pos % 4;
        for (int i = 0; i < 4; ++i) {
            int nx = x + dx[i], ny = y + dy[i];
            if (nx >= 0 && nx < 4 && ny >= 0 && ny < 4) {
                vector<int> new_state = current.state;
                swap(new_state[current.zero_pos], new_state[nx * 4 + ny]);
                int new_heuristic = manhattan_distance(new_state);
                if (new_heuristic + current.moves + 1 <= 45) { // Limit moves to ensure optimal path within 45 steps
                    pq.push({new_state, nx * 4 + ny, current.moves + 1, new_heuristic});
                }
            }
        }
    }
    return -1;
}

int main() {
    vector<int> start(16);
    for (int i = 0; i < 16; ++i) {
        cin >> start[i];
    }
    cout << a_star(start) << endl;
    return 0;
}
accuracy: 1
time_ms: 1820
memory_kb: 152432
final_rank: 14