結果

問題 No.5015 Escape from Labyrinth
ユーザー platinum
提出日時 2023-04-15 03:32:48
言語 C++17(gcc12)
(gcc 12.3.0 + boost 1.87.0)
結果
WA  
実行時間 -
コード長 25,029 bytes
コンパイル時間 5,445 ms
コンパイル使用メモリ 228,144 KB
実行使用メモリ 336,220 KB
スコア 147,000
最終ジャッジ日時 2023-04-15 11:45:41
合計ジャッジ時間 121,646 ms
ジャッジサーバーID
(参考情報)
judge11 / judge12
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 98 WA * 2
権限があれば一括ダウンロードができます

ソースコード

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 < 200 + D * 20){
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;
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
0