結果

問題 No.5015 Escape from Labyrinth
ユーザー ntk-ta01ntk-ta01
提出日時 2023-04-15 14:33:54
言語 Rust
(1.77.0 + proconio)
結果
WA  
実行時間 -
コード長 14,007 bytes
コンパイル時間 4,035 ms
コンパイル使用メモリ 163,288 KB
実行使用メモリ 13,220 KB
スコア 6,470
最終ジャッジ日時 2023-04-15 14:34:45
合計ジャッジ時間 50,195 ms
ジャッジサーバーID
(参考情報)
judge14 / judge15
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 WA -
testcase_01 WA -
testcase_02 WA -
testcase_03 WA -
testcase_04 WA -
testcase_05 WA -
testcase_06 WA -
testcase_07 WA -
testcase_08 WA -
testcase_09 WA -
testcase_10 WA -
testcase_11 WA -
testcase_12 WA -
testcase_13 WA -
testcase_14 WA -
testcase_15 WA -
testcase_16 WA -
testcase_17 WA -
testcase_18 WA -
testcase_19 WA -
testcase_20 WA -
testcase_21 WA -
testcase_22 WA -
testcase_23 WA -
testcase_24 WA -
testcase_25 WA -
testcase_26 WA -
testcase_27 AC 895 ms
5,252 KB
testcase_28 WA -
testcase_29 WA -
testcase_30 WA -
testcase_31 WA -
testcase_32 WA -
testcase_33 WA -
testcase_34 WA -
testcase_35 WA -
testcase_36 AC 480 ms
4,372 KB
testcase_37 WA -
testcase_38 WA -
testcase_39 WA -
testcase_40 AC 436 ms
4,368 KB
testcase_41 WA -
testcase_42 WA -
testcase_43 WA -
testcase_44 WA -
testcase_45 WA -
testcase_46 WA -
testcase_47 WA -
testcase_48 WA -
testcase_49 WA -
testcase_50 WA -
testcase_51 AC 519 ms
4,372 KB
testcase_52 WA -
testcase_53 WA -
testcase_54 WA -
testcase_55 WA -
testcase_56 WA -
testcase_57 WA -
testcase_58 WA -
testcase_59 WA -
testcase_60 WA -
testcase_61 WA -
testcase_62 WA -
testcase_63 WA -
testcase_64 WA -
testcase_65 WA -
testcase_66 WA -
testcase_67 WA -
testcase_68 WA -
testcase_69 WA -
testcase_70 WA -
testcase_71 WA -
testcase_72 WA -
testcase_73 WA -
testcase_74 WA -
testcase_75 WA -
testcase_76 WA -
testcase_77 WA -
testcase_78 WA -
testcase_79 WA -
testcase_80 WA -
testcase_81 WA -
testcase_82 WA -
testcase_83 AC 577 ms
4,372 KB
testcase_84 WA -
testcase_85 WA -
testcase_86 WA -
testcase_87 WA -
testcase_88 WA -
testcase_89 WA -
testcase_90 WA -
testcase_91 WA -
testcase_92 WA -
testcase_93 WA -
testcase_94 WA -
testcase_95 WA -
testcase_96 WA -
testcase_97 AC 391 ms
4,368 KB
testcase_98 WA -
testcase_99 WA -
権限があれば一括ダウンロードができます

ソースコード

diff #

// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        let s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};

    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };

    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };

    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };

    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };

    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}

use std::{
    cell::RefCell,
    rc::{Rc, Weak},
};

const DIJ: [(usize, usize); 4] = [(0, !0), (!0, 0), (0, 1), (1, 0)];
const DIR: [char; 4] = ['L', 'U', 'R', 'D'];

fn main() {
    let (input, map) = parse_input();
    let out = beam_search(&input, &map);
    write_output(&out);
}

fn beam_search(input: &Input, map: &[Vec<Square>]) -> Output {
    const BEAM_WIDTH: usize = 1000;
    let mut tree = {
        let state = State::new(input, map.to_vec());
        let head = Rc::new(Node::new(None));
        BeamSearchTree { state, head }
    };
    let mut current_queue = vec![tree.head.clone()];
    let mut beam_queue = (0..input.h + 1).map(|_| vec![]).collect::<Vec<_>>();
    // let mut hash_set = FxHashSet::default();
    for turn in 0..input.h {
        tree.dfs(input, &mut beam_queue, turn, true);
        current_queue.clear();
        if turn + 1 == input.h {
            // 最終ターンなのでcandidatesを作らない
            break;
        }
        let mut candidates = vec![];
        std::mem::swap(&mut candidates, &mut beam_queue[turn + 1]);
        if candidates.len() > BEAM_WIDTH {
            candidates.select_nth_unstable_by_key(BEAM_WIDTH - 1, |c| std::cmp::Reverse(c.score));
            candidates.truncate(BEAM_WIDTH);
        }
        // hash_set.clear();
        for candidate in candidates {
            // プレイヤーの位置が同じ候補を重複除去
            // if !hash_set.insert(candidate.hash) {
            //     continue;
            // }
            let child = Node::new(Some(candidate.parent.clone()));
            let child_ptr = Rc::new(child);
            let children = &mut candidate.parent.children.borrow_mut();
            children.push((candidate.act, Rc::downgrade(&child_ptr)));
            current_queue.push(child_ptr);
        }
    }
    let last_candidates = beam_queue.pop().unwrap();
    let best_candidate = last_candidates
        .into_iter()
        .max_by_key(|state| state.score)
        .unwrap();

    // 復元
    let mut commands = vec![DIR[best_candidate.act.dir]];
    commands.reserve(input.h);
    let mut parent = best_candidate.parent;
    while let Some(p) = parent.parent.clone() {
        {
            let children = &mut p.children.borrow_mut();
            let (act, _) = children
                .iter()
                .find(|(_, child)| child.upgrade().is_some())
                .unwrap();
            commands.push(DIR[act.dir]);
        }
        parent = p;
    }
    commands.reverse();
    Output::new(commands)
}

#[derive(Clone)]
struct State {
    score: i32,
    map: Vec<Vec<Square>>,
    pos: (usize, usize),
    visited: Vec<Vec<bool>>,
    has_key: bool,
    turn: usize,
    // hash: u64,
}

impl State {
    fn new(input: &Input, map: Vec<Vec<Square>>) -> Self {
        let pos = find_player_position(&map);
        let mut visited = vec![vec![false; input.n]; input.n];
        visited[pos.0][pos.1] = true;
        State {
            score: 0,
            map,
            pos,
            visited,
            has_key: false,
            turn: 0,
            // hash,
        }
    }

    fn apply(&mut self, act: &Action) {
        self.turn += act.consume_turn;
        self.score += act.score_diff;
        // self.hash ^= act.hash_diff;

        let (player_r, player_c) = &mut self.pos;
        let next_r = *player_r + DIJ[act.dir].0;
        let next_c = *player_c + DIJ[act.dir].1;
        if act.is_moved {
            *player_r = next_r;
            *player_c = next_c;
        }
        if act.is_new_visited {
            self.visited[*player_r][*player_c] = true;
        }
        if act.is_got_key {
            self.has_key = true;
        }
    }

    fn revert(&mut self, act: &Action) {
        self.turn -= act.consume_turn;
        self.score -= act.score_diff;
        // self.hash ^= act.hash_diff;
        let rev_dir = (act.dir + 2) % 4;

        let (player_r, player_c) = &mut self.pos;
        let prev_r = *player_r + DIJ[rev_dir].0;
        let prev_c = *player_c + DIJ[rev_dir].1;
        if act.is_got_key {
            self.has_key = false;
        }
        if act.is_new_visited {
            self.visited[*player_r][*player_c] = false;
        }
        if act.is_moved {
            *player_r = prev_r;
            *player_c = prev_c;
        }
    }
}

struct Action {
    dir: usize,
    is_moved: bool,
    is_new_visited: bool,
    is_got_key: bool,
    score_diff: i32,
    consume_turn: usize,
    // hash_diff: u64,
}

impl Action {
    fn new(
        dir: usize,
        is_moved: bool,
        is_new_visited: bool,
        is_got_key: bool,
        score_diff: i32,
        consume_turn: usize,
        // hash_diff: u64,
    ) -> Self {
        Action {
            dir,
            is_moved,
            is_new_visited,
            is_got_key,
            score_diff,
            consume_turn,
            // hash_diff,
        }
    }
}

struct Candidate {
    score: i32,
    parent: Rc<Node>,
    act: Action,
    // hash: u64,
}

impl Candidate {
    // fn new(score: i32, parent: Rc<Node>, act: Action, hash: u64) -> Self {
    fn new(score: i32, parent: Rc<Node>, act: Action) -> Self {
        Candidate {
            score,
            parent,
            act,
            // hash,
        }
    }
}

struct Node {
    parent: Option<Rc<Node>>,
    children: RefCell<Vec<(Action, Weak<Node>)>>,
}

impl Node {
    fn new(parent: Option<Rc<Node>>) -> Self {
        let children = RefCell::new(vec![]);
        Self { parent, children }
    }
}

struct BeamSearchTree {
    state: State,
    head: Rc<Node>, // 木上の探索中のnodeを保持する
}

impl BeamSearchTree {
    fn dfs(
        &mut self,
        input: &Input,
        beam_queue: &mut Vec<Vec<Candidate>>,
        target_turn: usize,
        is_single_path: bool,
    ) {
        if self.state.turn == target_turn {
            for (dir, &(dr, dc)) in DIJ.iter().enumerate() {
                let mut score_diff = 0;
                let mut is_new_visited = false;
                let mut is_got_key = false;
                // let mut hash_diff = 0;

                let (player_r, player_c) = &self.state.pos;
                let next_r = *player_r + dr;
                let next_c = *player_c + dc;
                if input.n <= next_r || input.n <= next_c {
                    continue;
                }
                if self.state.map[next_r][next_c] == Square::Wall
                    || self.state.map[next_r][next_c] == Square::Finder
                {
                    continue;
                }
                let is_moved = true;
                if !self.state.visited[next_r][next_c] {
                    is_new_visited = true;
                    if self.state.map[next_r][next_c] == Square::Jewelry {
                        score_diff += 10;
                    }
                    if self.state.map[next_r][next_c] == Square::Key {
                        is_got_key = true;
                        score_diff += (input.h - self.state.turn) as i32 * 2;
                    }
                    if self.state.map[next_r][next_c] == Square::Door {
                        if self.state.has_key {
                            score_diff += (input.h - self.state.turn) as i32 * 2;
                        } else {
                            score_diff -= (input.h - self.state.turn) as i32 * 2;
                        }
                    }
                    // else if self.state.map[next_r][next_c] == Square::Finder {
                    // score_diff -= (input.t - self.state.turn) as i32 / 2;
                    // }
                }
                // hash_diff ^= input.player_hashes[*player_r][*player_c];
                // hash_diff ^= input.player_hashes[next_r][next_c];

                // next_turnが+1じゃないアクションが存在する問題のために
                // 下のif文を書いたり、beam_queueの長さをinput.t+1にする
                let consume_turn = || -> usize {
                    if self.state.map[next_r][next_c] == Square::Door {
                        return input.h - self.state.turn;
                    }
                    // 探知機にひっかかっていたらD減る
                    // TODO: 引っかかっているか調べるやつを書く
                    1
                }();
                let next_turn = self.state.turn + consume_turn;
                if next_turn < beam_queue.len() {
                    beam_queue[next_turn].push(Candidate::new(
                        self.state.score + score_diff,
                        self.head.clone(),
                        Action::new(
                            dir,
                            is_moved,
                            is_new_visited,
                            is_got_key,
                            score_diff,
                            consume_turn,
                        ),
                        // self.state.hash ^ hash_diff,
                    ));
                }
            }
        } else {
            let node = self.head.clone();

            let next_is_single_path = {
                let children = &mut node.children.borrow_mut();
                children.retain(|(_, child)| child.upgrade().is_some());
                if children.is_empty() {
                    false
                } else {
                    let next_is_single_path = is_single_path && (children.len() == 1);
                    for (act, child) in children.iter_mut() {
                        let next_turn = self.state.turn + 1;
                        if next_turn > target_turn {
                            continue;
                        }

                        self.head = child.upgrade().unwrap();
                        self.state.apply(act);

                        self.dfs(input, beam_queue, target_turn, next_is_single_path);

                        if !next_is_single_path {
                            self.state.revert(act);
                        }
                    }
                    next_is_single_path
                }
            };

            // nodeの子が1つでなければ、headを元のnodeに戻す
            if !next_is_single_path {
                self.head = node;
            }
        }
    }
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
enum Square {
    Empty,
    Wall,
    Player,
    Door,
    Key,
    Jewelry,
    Fire,
    Finder,
}

fn find_player_position(map: &[Vec<Square>]) -> (usize, usize) {
    for (r, row) in map.iter().enumerate() {
        for (c, square) in row.iter().enumerate() {
            if *square == Square::Player {
                return (r, c);
            }
        }
    }
    unreachable!("map上にplayerが見つかりませんでした")
}

fn parse_input() -> (Input, Vec<Vec<Square>>) {
    input! {
        n: usize,
        d: usize,
        h: usize,
        grid: [String; n],
        m: usize,
        yxd: [(usize, usize, usize); m],
    }
    (
        Input {
            n,
            d,
            h,
            m,
            finders: yxd
                .into_iter()
                .map(|(y, x, d)| Finder::new(y, x, d))
                .collect(),
        },
        grid.into_iter()
            .map(|row| {
                row.chars()
                    .map(|ch| match ch {
                        '.' => Square::Empty,
                        '#' => Square::Wall,
                        'S' => Square::Player,
                        'G' => Square::Door,
                        'K' => Square::Key,
                        'J' => Square::Jewelry,
                        'F' => Square::Fire,
                        'E' => Square::Finder,
                        _ => unreachable!("想定外の入力がありました"),
                    })
                    .collect()
            })
            .collect(),
    )
}

#[allow(dead_code)]
#[derive(Debug)]
struct Input {
    n: usize,             // グリッドの大きさ
    d: usize,             // 探知機によって削られる体力
    h: usize,             // プレイヤーの初期体力
    m: usize,             // 探知機の数
    finders: Vec<Finder>, // 探知機の位置と作動感覚
}

#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Finder {
    y: usize, // y座標
    x: usize, // x座標
    d: usize, // 作動間隔
}

impl Finder {
    fn new(y: usize, x: usize, d: usize) -> Self {
        Finder { y, x, d }
    }
}

#[derive(Clone)]
struct Output {
    commands: Vec<char>,
}

impl Output {
    fn new(commands: Vec<char>) -> Self {
        Output { commands }
    }
}

fn write_output(out: &Output) {
    for cmd in out.commands.iter() {
        println!("M {}", cmd);
    }
}
0