結果

問題 No.5015 Escape from Labyrinth
ユーザー platinumplatinum
提出日時 2023-04-15 03:45:05
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
WA  
実行時間 -
コード長 25,029 bytes
コンパイル時間 5,152 ms
コンパイル使用メモリ 228,656 KB
実行使用メモリ 305,408 KB
スコア 142,540
最終ジャッジ日時 2023-04-15 11:47:41
合計ジャッジ時間 114,391 ms
ジャッジサーバーID
(参考情報)
judge11 / judge12
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1,175 ms
286,916 KB
testcase_01 AC 1,041 ms
207,460 KB
testcase_02 AC 943 ms
210,336 KB
testcase_03 AC 937 ms
137,288 KB
testcase_04 AC 775 ms
149,640 KB
testcase_05 AC 1,133 ms
208,484 KB
testcase_06 AC 1,123 ms
232,144 KB
testcase_07 AC 945 ms
163,104 KB
testcase_08 AC 941 ms
140,864 KB
testcase_09 AC 979 ms
150,996 KB
testcase_10 AC 1,070 ms
206,672 KB
testcase_11 AC 1,005 ms
238,624 KB
testcase_12 AC 993 ms
189,172 KB
testcase_13 AC 906 ms
154,104 KB
testcase_14 AC 980 ms
140,184 KB
testcase_15 AC 1,019 ms
208,820 KB
testcase_16 AC 1,061 ms
192,672 KB
testcase_17 AC 922 ms
202,964 KB
testcase_18 AC 840 ms
175,828 KB
testcase_19 AC 845 ms
160,788 KB
testcase_20 AC 1,172 ms
208,860 KB
testcase_21 AC 1,103 ms
190,156 KB
testcase_22 AC 1,010 ms
194,072 KB
testcase_23 AC 974 ms
171,612 KB
testcase_24 AC 898 ms
138,084 KB
testcase_25 AC 1,142 ms
225,648 KB
testcase_26 AC 1,053 ms
233,208 KB
testcase_27 AC 1,006 ms
173,632 KB
testcase_28 AC 911 ms
186,052 KB
testcase_29 AC 881 ms
169,232 KB
testcase_30 AC 1,163 ms
273,276 KB
testcase_31 AC 1,148 ms
228,840 KB
testcase_32 AC 1,001 ms
158,964 KB
testcase_33 AC 954 ms
135,120 KB
testcase_34 AC 769 ms
138,880 KB
testcase_35 AC 1,211 ms
252,336 KB
testcase_36 AC 1,054 ms
239,492 KB
testcase_37 AC 1,004 ms
205,016 KB
testcase_38 AC 848 ms
156,196 KB
testcase_39 AC 766 ms
145,088 KB
testcase_40 AC 1,198 ms
267,372 KB
testcase_41 AC 1,030 ms
255,020 KB
testcase_42 AC 1,076 ms
236,436 KB
testcase_43 AC 819 ms
168,440 KB
testcase_44 AC 758 ms
149,860 KB
testcase_45 AC 1,011 ms
202,444 KB
testcase_46 AC 1,025 ms
177,824 KB
testcase_47 AC 1,013 ms
211,652 KB
testcase_48 AC 867 ms
153,680 KB
testcase_49 AC 997 ms
143,912 KB
testcase_50 AC 1,176 ms
286,740 KB
testcase_51 AC 1,043 ms
176,512 KB
testcase_52 AC 1,003 ms
174,984 KB
testcase_53 AC 1,037 ms
171,224 KB
testcase_54 AC 908 ms
137,136 KB
testcase_55 AC 1,048 ms
198,260 KB
testcase_56 AC 1,096 ms
245,972 KB
testcase_57 AC 1,104 ms
187,640 KB
testcase_58 AC 812 ms
147,060 KB
testcase_59 AC 872 ms
173,132 KB
testcase_60 AC 1,165 ms
271,664 KB
testcase_61 AC 1,003 ms
227,572 KB
testcase_62 AC 1,018 ms
156,040 KB
testcase_63 AC 907 ms
163,072 KB
testcase_64 AC 942 ms
140,976 KB
testcase_65 AC 1,244 ms
205,132 KB
testcase_66 AC 998 ms
195,728 KB
testcase_67 AC 1,022 ms
203,732 KB
testcase_68 AC 903 ms
186,680 KB
testcase_69 AC 814 ms
157,084 KB
testcase_70 AC 1,236 ms
305,408 KB
testcase_71 AC 1,110 ms
182,172 KB
testcase_72 AC 1,023 ms
184,728 KB
testcase_73 AC 926 ms
136,936 KB
testcase_74 AC 807 ms
133,348 KB
testcase_75 AC 1,142 ms
274,736 KB
testcase_76 AC 953 ms
159,676 KB
testcase_77 AC 914 ms
165,260 KB
testcase_78 AC 939 ms
181,144 KB
testcase_79 AC 888 ms
135,772 KB
testcase_80 AC 1,185 ms
299,352 KB
testcase_81 AC 1,121 ms
224,168 KB
testcase_82 AC 1,213 ms
183,948 KB
testcase_83 AC 902 ms
192,024 KB
testcase_84 AC 960 ms
143,360 KB
testcase_85 AC 1,186 ms
214,248 KB
testcase_86 AC 1,037 ms
214,560 KB
testcase_87 AC 1,109 ms
225,812 KB
testcase_88 AC 924 ms
200,444 KB
testcase_89 AC 865 ms
168,492 KB
testcase_90 AC 979 ms
240,800 KB
testcase_91 AC 1,052 ms
240,088 KB
testcase_92 WA -
testcase_93 AC 1,026 ms
167,876 KB
testcase_94 AC 841 ms
157,288 KB
testcase_95 AC 1,081 ms
263,096 KB
testcase_96 AC 1,003 ms
210,756 KB
testcase_97 AC 1,060 ms
200,332 KB
testcase_98 AC 933 ms
122,120 KB
testcase_99 AC 1,021 ms
142,536 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <sstream>
#include <algorithm>
#include <set>
#include <unordered_map>
#include <random>
#include <cassert>

using namespace std;
using pii = pair<int,int>;

const int grid_size = 60;
const int max_hp = 1500;
const int jewel_value = 10;
int dy[5] = {-1, 1, 0, 0, 0};
int dx[5] = {0, 0, -1, 1, 0};
string dir = "UDLRS";
const int INF = (int)1e9;

const int testcase = 1;

enum class file_status{
    local,
    score,
    submit,
};
file_status now_status = file_status::submit;

void read_input(){
    std::stringstream ss;
    std::string num = std::to_string(testcase);
    int siz = num.size();
    for(int i = 0; i < 3 - siz; i++) num = '0' + num;
    ss << "in/testcase_" << num << ".txt";
    FILE *in = freopen(ss.str().c_str(), "r", stdin);
}
void file_output(){
    std::stringstream ss;
    std::string num = std::to_string(testcase);
    int siz = num.size();
    for(int i = 0; i < 3 - siz; i++) num = '0' + num;
    ss << "test_out/testcase_" << num << ".txt";
    FILE *out = freopen(ss.str().c_str(), "w", stdout);
}

// セルの情報の候補
enum class cell_status{
    empty,
    wall,
    block,
    enemy,
    key,
    goal,
    fire,
    jewel,
    player,
    cell_kinds,
};
int GetCellStatusNumber(cell_status st){
    if(st == cell_status::empty) return 0;
    else if(st == cell_status::wall) return 1;
    else if(st == cell_status::block) return 2;
    else if(st == cell_status::enemy) return 3;
    else if(st == cell_status::key) return 4;
    else if(st == cell_status::goal) return 5;
    else if(st == cell_status::fire) return 6;
    else if(st == cell_status::jewel) return 7;
    else if(st == cell_status::cell_kinds) return 8;
    return -1;
}

// 探知機の情報
struct Enemy{
    int y, x, d, num;
    bool destroyed;
    Enemy(int y, int x, int d, int num){
        this->y = y, this->x = x, this->d = d;
        this-> num = num;
        destroyed = false;
    }
};

// セルの情報
struct Cell{
    cell_status status;
    Enemy* e_pointer = nullptr;
    Cell(cell_status st = cell_status::empty) : status(st) {}
};

template<typename T>
struct v2{
    std::vector<std::vector<T>> v;
    v2(){}
    v2(int x){
        v.resize(x);
    }
    v2(int x, int y){
        v.resize(x, std::vector<T>(y));
    }
    v2(int x, int y, T val){
        v.resize(x, std::vector<T>(y, val));
    }
    std::vector<T>& operator [] (const int num){
        return v[num];
    }
    void init(int x){
        v.resize(x);
    }
    void init(int x, int y){
        v.resize(x, std::vector<T>(y));
    }
    void init(int x, int y, T val){
        v.resize(x, std::vector<T>(y, val));
    }
    void push_back(std::vector<T>& e){
        v.emplace_back(e);
    }
    int size(){
        return (int)v.size();
    }
    bool empty(){
        return v.empty();
    }
    void clear(){
        v.clear();
    }
    void erase(int pos){
        v.erase(v.begin() + pos);
    }
};

template<typename T>
struct v3{
    std::vector<v2<T>> v;
    v3(){}
    v3(int x){
        v.resize(x);
    }
    v3(int x, int y){
        v.resize(x, v2<T>(y));
    }
    v3(int x, int y, int z){
        v.resize(x, v2<T>(y, z));
    }
    v3(int x, int y, int z, T val){
        v.resize(x, v2<T>(y, z, val));
    }
    v2<T>& operator [] (const int num){
        return v[num];
    }
    void init(int x, int y){
        v.resize(x, v2<T>(y));
    }
    void init(int x, int y, int z){
        v.resize(x, v2<T>(y, z));
    }
    void push_back(v2<T>& e){
        v.emplace_back(e);
    }
    bool empty(){
        return v.empty();
    }
    void clear(){
        v.clear();
    }
};

// ソートされた配列からの探索
bool is_included(vector<int> &vec, int val){
    int find = upper_bound(vec.begin(), vec.end(), val)
    - lower_bound(vec.begin(), vec.end(), val);
    if(find) return true;
    else return false;
}

random_device rnd;
mt19937 engine(rnd());
vector<long long> hash_list;
void hash_init(){
    uniform_int_distribution<> h(1, (int)1e9);
    int kinds_num = GetCellStatusNumber(cell_status::cell_kinds);
    for(int i = 0; i < grid_size * grid_size * kinds_num; i++){
        long long val1 = h(engine);
        long long val2 = h(engine);
        hash_list.emplace_back(val1 * val2);
    }
}
long long get_hash(int y, int x, cell_status st){
    int kinds_num = GetCellStatusNumber(cell_status::cell_kinds);
    int num = (y * grid_size + x) * kinds_num + GetCellStatusNumber(st);
    return hash_list[num];
}
long long zobrist_hash(v2<Cell>& now_grid){
    long long res = 0;
    for(int i = 0; i < grid_size; i++){
        for(int j = 0; j < grid_size; j++){
            res ^= get_hash(i, j, now_grid[i][j].status);
        }
    }
    return res;
}
long long hash_update(int ny, int nx, v2<Cell>& now_grid){
    long long res = 0;
    long long o = get_hash(ny, nx, now_grid[ny][nx].status);
    long long n = get_hash(ny, nx, cell_status::empty);
    res = o ^ n;
    return res;
}
unordered_map<long long, v2<Cell>> Grids;

// ダイクストラ法で用いる情報
struct pathway{
    int dist;
    int y, x, t;
    long long hash;
    pathway(int d, int y, int x, int t, long long h) :
    dist(d), y(y), x(x), t(t), hash(h) {}
    bool operator> (const pathway& p) const{
        return dist > p.dist;
    }
};

int N, D, H, M;
vector<Enemy> enemy;
vector<string> S;
v2<Cell> grid;

int sy, sx, ky, kx, gy, gx;

v2<int> enemy_number;
v2<int> jewel_number;
vector<pii> jewel_pos;
v2<int> fire_number;
vector<pii> fire_pos;

// 範囲外かどうか
bool range_out(int y, int x){
    if(y < 0 || y >= grid_size) return true;
    if(x < 0 || x >= grid_size) return true;
    return false;
}

// プレイヤーの情報
struct Player{
    int y, x;
    int hp, fire;
    int score;
    bool get_key, using_magic;
    Player() {}
    Player(int y, int x) : y(y), x(x){
        hp = max_hp;
        fire = 0;
        score = 0;
        get_key = false;
        using_magic = false;
    }
    int move(int k){
        int ny = y + dy[k], nx = x + dx[k];
        if(range_out(ny, nx)) return -1;
        if(grid[ny][nx].status == cell_status::wall
        || grid[ny][nx].status == cell_status::block
        || grid[ny][nx].status == cell_status::enemy){
            return -1;
        }
        if(grid[ny][nx].status == cell_status::fire){
            fire++;
        }
        else if(grid[ny][nx].status == cell_status::jewel){
            score += jewel_value;
        }
        else if(grid[ny][nx].status == cell_status::key){
            get_key = true;
        }
        y = ny; x = nx;
        return 0;
    }
    void damage(int d){
        hp -= d;
    }
};

// 方向を取得
int get_direction(char d){
    for(int k = 0; k < 4; k++){
        if(d == dir[k]) return k;
    }
    return -1;
}

void input(){
    cin >> N >> D >> H;

    S.resize(N);
    for(int i = 0; i < N; i++) cin >> S[i];
    enemy_number.init(N, N, -1);
    jewel_number.init(N, N, -1);
    fire_number.init(N, N, -1);

    cin >> M;
    for(int i = 0; i < M; i++){
        int y, x, d;
        cin >> y >> x >> d;
        enemy.emplace_back(y, x, d, i);
        enemy_number[y][x] = i;
    }

    grid.init(N, N, cell_status::empty);
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            if(S[i][j] == '#'){
                grid[i][j].status = cell_status::wall;
            }
            else if(S[i][j] == 'E'){
                grid[i][j].status = cell_status::enemy;
                grid[i][j].e_pointer = &enemy[enemy_number[i][j]];
            }
            else if(S[i][j] == 'K'){
                grid[i][j].status = cell_status::key;
            }
            else if(S[i][j] == 'G'){
                grid[i][j].status = cell_status::goal;
            }
            else if(S[i][j] == 'F'){
                grid[i][j].status = cell_status::fire;
                fire_number[i][j] = (int)fire_pos.size();
                fire_pos.emplace_back(i, j);
            }
            else if(S[i][j] == 'J'){
                grid[i][j].status = cell_status::jewel;
                jewel_number[i][j] = (int)jewel_pos.size();
                jewel_pos.emplace_back(i, j);
            }
            else if(S[i][j] == 'S'){
                grid[i][j].status = cell_status::player;
            }
            else{
                grid[i][j].status = cell_status::empty;
            }
        }
    }
}

uniform_int_distribution<> rand_eval(-5, 5);
struct node{
    Player player;
    int now_turn;
    int eval;
    vector<string> actions;
    // 盤面の記録
    set<int> block_pos; //座標
    vector<int> picked_jewel; //番号
    vector<int> picked_fire; //番号
    vector<int> enemy_destroyed; //番号

    node(Player player){
        this->player = player;
        now_turn = 0;
        eval = 0;
    }
    bool operator< (const node& v) const{
        return eval < v.eval;
    }
};
int get_last_move(node &v){
    if(v.actions.empty() || v.actions.back() == "S") return -1;
    else{
        if(v.actions.back().at(0) != 'M') return -1;
        else return get_direction(v.actions.back().at(2));
    }
}
void update_eval(node &v){
    v.eval = v.player.score;
    v.eval -= (max_hp - v.player.hp) * 0.1 * (1 - v.player.get_key);
    v.eval += rand_eval(engine);
}
void update_actions(node& v, string& query){
    v.now_turn++;
    v.actions.emplace_back(query);
    char c = query[0];
    if(c == 'S') return;
    int k = get_direction(query[2]);
    if(c == 'M'){
        int y = v.player.y + dy[k];
        int x = v.player.x + dx[k];
        cell_status st = grid[y][x].status;
        if(st == cell_status::jewel){
            int j_num = jewel_number[y][x];
            for(auto &num : v.picked_jewel){
                if(j_num == num) return;
            }
            v.picked_jewel.emplace_back(j_num);
            v.player.score += jewel_value;
        }
        else if(st == cell_status::fire){
            int f_num = fire_number[y][x];
            for(auto &num : v.picked_fire){
                if(f_num == num) return;
            }
            v.picked_fire.emplace_back(f_num);
            v.player.fire++;
        }
        else if(st == cell_status::key){
            v.player.get_key = true;
        }
    }
    else if(c == 'B'){
        int y = v.player.y + dy[k];
        int x = v.player.x + dx[k];
        auto itr = v.block_pos.find(y * grid_size + x);
        if(itr == v.block_pos.end()){
            v.block_pos.insert(y * grid_size + x);
        }
        else{
            v.block_pos.erase(itr);
        }
    }
    else if(c == 'F'){
        assert(v.player.fire > 0);
        v.player.fire--;
        int y = v.player.y + dy[k];
        int x = v.player.x + dx[k];
        while(!range_out(y, x)){
            if(grid[y][x].status == cell_status::wall){
                break;
            }
            else if(grid[y][x].status == cell_status::enemy){
                int e_num = enemy_number[y][x];
                bool destroyed = false;
                for(auto &num : v.enemy_destroyed){
                    if(e_num == num){
                        destroyed = true;
                        break;
                    }
                }
                if(destroyed) continue;
                else{
                    v.enemy_destroyed.emplace_back(e_num);
                    break;
                }
            }
            else{
                auto itr = v.block_pos.find(y * grid_size + x);
                if(itr == v.block_pos.end()) continue;
                else break;
            }
        }
    }
}

// プレイヤーへのダメージ計算
int CalcDamage(node& v){
    int res = 1;
    for(int k = 0; k < 4; k++){
        int y = v.player.y + dy[k], x = v.player.x + dx[k];
        while(!range_out(y, x)){
            if(grid[y][x].status == cell_status::wall){
                break;
            }
            else if(grid[y][x].status == cell_status::enemy){
                int e_num = enemy_number[y][x];
                bool destroyed = false;
                for(auto &num : v.enemy_destroyed){
                    if(e_num == num){
                        destroyed = true;
                        break;
                    }
                }
                if(!destroyed){
                    if(v.now_turn % grid[y][x].e_pointer->d == 0){
                        res += D;
                    }
                    break;
                }
            }
            else{
                if(v.block_pos.find(y * grid_size * x) != v.block_pos.end()){
                    break;
                }
            }
            y += dy[k]; x += dx[k];
        }
    }
    return res;
}
int CalcDamage(int py, int px, int t, v2<Cell>& now_grid){
    int res = 1;
    for(int k = 0; k < 4; k++){
        int y = py + dy[k], x = px + dx[k];
        while(!range_out(y, x)){
            if(now_grid[y][x].status == cell_status::wall){
                break;
            }
            if(now_grid[y][x].status == cell_status::block){
                break;
            }
            if(now_grid[y][x].status == cell_status::enemy){
                if(t > 0 && t % now_grid[y][x].e_pointer->d == 0){
                    res += D;
                }
                break;
            }
            y += dy[k]; x += dx[k];
        }
    }
    return res;
}

vector<pii> FindTarget(node& v, int number = 5){
    vector<pii> targets;
    int py = v.player.y, px = v.player.x;

    // 近くにあれば鍵を取りに行く
    int dist_key = abs(py - ky) + abs(px - kx);
    if(!v.player.get_key && dist_key <= 20){
        targets.emplace_back(ky, kx);
        return targets;
    }

    // number個になるまで宝石か火薬を探す
    vector<pii> jewel_fire_list; // first = マンハッタン距離, second = 座標
    vector<int> J = v.picked_jewel;
    vector<int> F = v.picked_fire;
    sort(J.begin(), J.end());
    sort(F.begin(), F.end());

    for(int i = 0; i < (int)jewel_pos.size(); i++){
        if(is_included(J, i)) continue;
        int jy = jewel_pos[i].first, jx = jewel_pos[i].second;
        int dist = abs(py - jy) + abs(px - jx);
        jewel_fire_list.emplace_back(dist, jy * grid_size + jx);
    }
    for(int i = 0; i < (int)fire_pos.size(); i++){
        if(is_included(F, i)) continue;
        int fy = fire_pos[i].first, fx = fire_pos[i].second;
        int dist = abs(py - fy) + abs(px - fx);
        jewel_fire_list.emplace_back(dist, fy * grid_size + fx);
    }
    sort(jewel_fire_list.begin(), jewel_fire_list.end());
    for(int i = 0; i < number; i++){
        int pos = jewel_fire_list[i].second;
        targets.emplace_back(pos / grid_size, pos % grid_size);
    }

    return targets;
}

vector<string> FindPath(node& v, pii& target_pos){
    int py = v.player.y, px = v.player.x;
    int ty = target_pos.first, tx = target_pos.second;
	/*
    // 探索範囲を限定する
    int min_y = min(py, ty) - 1, max_y = max(py, ty) + 1;
    int min_x = min(px, tx) - 1, max_x = max(px, tx) + 1;
    if(min_y < 0) min_y = 0;
    if(min_x < 0) min_x = 0;
    if(max_y >= grid_size) max_y = grid_size - 1;
    if(max_x >= grid_size) max_x = grid_size - 1;
    int wy = max_y - min_y + 1, wx = max_x - min_x + 1;
	*/
	int min_y = 0, min_x = 0;
	int max_y = grid_size - 1, max_x = grid_size - 1;

    v2<int> dist(N, N, -1);
    dist[py-min_y][px-min_x] = 0;
    queue<int> q;
    q.emplace(py * grid_size + px);
    while(!q.empty()){
        if(dist[ty-min_y][tx-min_x] != -1) break;
        int pos = q.front(); q.pop();
        int y = pos / grid_size, x = pos % grid_size;
        for(int k = 0; k < 4; k++){
            int ny = y + dy[k], nx = x + dx[k];
            if(ny < min_y || nx < min_x) continue;
            if(ny > max_y || nx > max_x) continue;
            if(dist[ny-min_y][nx-min_x] != -1) continue;
            cell_status cell = grid[ny][nx].status;
            if(cell == cell_status::wall){
                continue;
            }
            else if(cell == cell_status::enemy){
                bool destroyed = false;
                int e_num = enemy_number[ny][nx];
                for(auto &num : v.enemy_destroyed){
                    if(e_num == num){
                        destroyed = true;
                        break;
                    }
                }
                if(!destroyed) continue;
            }
            else{
                auto itr = v.block_pos.find(ny * grid_size + nx);
                if(itr != v.block_pos.end()) continue;
            }
            dist[ny-min_y][nx-min_x] = dist[y-min_y][x-min_x] + 1;
            q.emplace(ny * grid_size + nx);
        }
    }
    // 経路復元
    vector<string> res;
    if(dist[ty-min_y][tx-min_x] == -1) return res;
    int now_y = ty, now_x = tx, now_d = dist[ty-min_y][tx-min_x];
    while(now_y != py || now_x != px){
        bool moved = false;
        for(int k = 0; k < 4; k++){
            int new_y = now_y + dy[k], new_x = now_x + dx[k];
            if(new_y < min_y || new_x < min_x) continue;
            if(new_y > max_y || new_x > max_x) continue;
            if(dist[new_y-min_y][new_x-min_x] != now_d - 1) continue;
            now_y = new_y, now_x = new_x;
            now_d--;
            string query = "M ";
            query += dir[k^1];
            res.push_back(query);
            moved = true;
            break;
        }
        assert(moved);
    }
    reverse(res.begin(), res.end());
    return res;
}

// ゴールまでの最小の体力減少値をダイクストラ法で見積もる
pair<int,vector<string>> CalcMinDistance(node& v, pii start, pii goal, bool debug = false){
    pair<int,vector<string>> res;

    // 最小公倍数 = 60 倍の頂点を用意する
    const int lcm = 60;
    int start_t = v.now_turn % lcm;
    v3<int> dist(N, N, lcm, INF);
    v3<string> prev_dir(N, N, lcm, "S");
    int start_y = start.first, start_x = start.second;
    int goal_y = goal.first, goal_x = goal.second;
    dist[start_y][start_x][start_t] = 0;
    priority_queue<pathway,vector<pathway>,greater<pathway>> pq;

    // 初期盤面を復元(破壊した探知機と生成したブロック)
    v2<Cell> init_grid = grid;
    for(auto &e_num : v.enemy_destroyed){
        Enemy e = enemy[e_num];
        init_grid[e.y][e.x].status = cell_status::empty;
    }
    for(auto b_num : v.block_pos){
        init_grid[b_num/grid_size][b_num%grid_size] = cell_status::block;
    }

    // 初期盤面をハッシュ化しておく
    long long init_hash = zobrist_hash(init_grid);
    Grids[init_hash] = init_grid;
    pq.emplace(0, start_y, start_x, start_t, init_hash);

    while(!pq.empty()){
        pathway pos = pq.top(); pq.pop();
        int d = pos.dist;
        int y = pos.y;
        int x = pos.x;
        if(y == goal_y && x == goal_x) continue;
        int t = pos.t;
        if(d != dist[y][x][t]) continue;
        // 上下左右への移動またはその場にとどまる
        for(int k = 0; k < 5; k++){
            int ny = y + dy[k], nx = x + dx[k];
            if(range_out(ny, nx)) continue;
            cell_status stat = Grids[pos.hash][ny][nx].status;
            if(stat == cell_status::enemy || stat == cell_status::wall){
                continue;
            }
            // ブロックを破壊する場合
            if(stat == cell_status::block && k < 4){
                v2<Cell> now_grid = Grids[pos.hash];
                long long change = hash_update(ny, nx, Grids[pos.hash]);
                long long new_hash = pos.hash ^ change;
                now_grid[ny][nx].status = cell_status::empty;

                int damage = CalcDamage(y, x, t + 1, now_grid)
                + CalcDamage(ny, nx, t + 2, now_grid);
                int nd = d + damage;
                int nt = (t + 2) % lcm;
                if(nd >= dist[ny][nx][nt]) continue;
                dist[ny][nx][nt] = nd;
                prev_dir[ny][nx][nt] = dir[k];
                prev_dir[ny][nx][nt].push_back(dir[k]);

                Grids[new_hash] = now_grid;
                pq.emplace(nd, ny, nx, nt, new_hash);
            }
            // 単に移動する場合
            else{
                int damage = CalcDamage(ny, nx, t + 1, Grids[pos.hash]);
                int nd = d + damage;
                int nt = (t + 1) % lcm;
                if(nd >= dist[ny][nx][nt]) continue;
                dist[ny][nx][nt] = nd;
                prev_dir[ny][nx][nt] = dir[k];
                pq.emplace(nd, ny, nx, nt, pos.hash);
            }
        }
    }

    int dist_min = INF, goal_t = -1;
    for(int t = 0; t < lcm; t++){
        int dist_t = dist[goal_y][goal_x][t];
        if(dist_min > dist_t){
            dist_min = dist_t;
            goal_t = t;
        }
    }
    res.first = dist_min;
    assert(goal_t != -1);

    // 経路復元
    vector<string> queries;
    int now_y = goal_y, now_x = goal_x, now_t = goal_t;
    if(debug) cout << "#debug start" << endl;
    while(now_y != start_y || now_x != start_x || now_t != start_t){
        if(debug){
            cout << "#t = " << now_t << endl;
            cout << "#(" << now_y  << ", " << now_x << ")" << endl;
            cout << "#d = " << dist[now_y][now_x][now_t] << endl;
        }
        char c = prev_dir[now_y][now_x][now_t][0];
        string query = "M ";
        if(c == 'S') query = c;
        else query += c;
        queries.push_back(query);
        int k = -1;
        if(c == 'S') k = 4;
        else{
            k = get_direction(c);
            k ^= 1;
        }
        // ブロックを破壊した場合
        if(prev_dir[now_y][now_x][now_t].size() > 1){
            c = prev_dir[now_y][now_x][now_t][1];
            string query = "B ";
            query += c;
            queries.push_back(query);
            now_t = (now_t - 1 + lcm) % lcm;
        }
        now_y += dy[k]; now_x += dx[k];
        now_t = (now_t - 1 + lcm) % lcm;
    }

    // デバッグ用の出力
    if(debug){
        cout << "#t = " << now_t << endl;
        cout << "#(" << now_y  << ", " << now_x << ")" << endl;
        cout << "#d = " << dist[now_y][now_x][now_t] << endl;
    }
    if(debug) cout << "#debug end" << endl;

    reverse(queries.begin(), queries.end());
    res.second = queries;
    return res;
}

node BeamSearch(node& init_node){
    node best_node = init_node;
    int best_eval = -INF;
    vector<priority_queue<node>> pq(max_hp);
    pq[0].emplace(init_node);
    const int beam_width = 5;
    
    for(int i = 0; i < max_hp; i++){
        for(int j = 0; j < beam_width; j++){
            if(pq[i].empty()) break;
            node v = pq[i].top(); pq[i].pop();
            
            if(v.player.hp < 500 && !v.player.get_key){
                continue;
            }
            // 余裕をもって打ち切り
            if(v.player.hp < 250 + D * 25){
                continue;
            }
            
            vector<pii> target = FindTarget(v);
            for(auto &pos : target){
                node nv = v;
                vector<string> path = FindPath(nv, pos);
                for(auto &query : path){
                    update_actions(nv, query);
                    if(query[0] == 'M'){
                        int k = get_direction(query[2]);
                        nv.player.move(k);
                    }
                    int d = CalcDamage(nv);
                    nv.player.damage(d);
                }

                // 最高評価なら更新
                update_eval(nv);
                if(nv.eval > best_eval){
                    best_eval = nv.eval;
                    best_node = nv;
                }

                int h = max_hp - nv.player.hp;
                pq[h].emplace(nv);
            }
        }
    }

    return best_node;
}

int main(){
    if(now_status == file_status::local){
        read_input();
        file_output();
    }
    input();
    hash_init();

    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            if(grid[i][j].status == cell_status::player){
                sy = i, sx = j;
            }
            else if(grid[i][j].status == cell_status::key){
                ky = i, kx = j;
            }
            else if(grid[i][j].status == cell_status::goal){
                gy = i, gx = j;
            }
        }
    }
    Player player(sy, sx);

    node init_node(player);
    node best_node = BeamSearch(init_node);
    pii start(best_node.player.y, best_node.player.x);
	pii key(ky, kx), goal(gy, gx);

    for(auto &query : best_node.actions){
        cout << query << endl;
    }
	if(!best_node.player.get_key){
		vector<string> path_to_key = CalcMinDistance(best_node, start, key).second;
		int siz = path_to_key.size();
		for(auto &query : path_to_key){
			cout << query << endl;
		}
		best_node.now_turn += siz;
		start = key;
	}
	vector<string> path_to_goal = CalcMinDistance(best_node, start, goal).second;
	for(auto &query : path_to_goal){
		cout << query << endl;
	}

    return 0;
}
0