結果

問題 No.5015 Escape from Labyrinth
ユーザー rhoorhoo
提出日時 2023-04-16 12:56:27
言語 Rust
(1.77.0 + proconio)
結果
WA  
実行時間 -
コード長 15,055 bytes
コンパイル時間 5,342 ms
コンパイル使用メモリ 180,364 KB
実行使用メモリ 165,356 KB
スコア 218,870
最終ジャッジ日時 2023-04-16 13:00:54
合計ジャッジ時間 256,123 ms
ジャッジサーバーID
(参考情報)
judge14 / judge15
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2,526 ms
145,936 KB
testcase_01 AC 2,155 ms
135,712 KB
testcase_02 AC 2,420 ms
150,384 KB
testcase_03 AC 2,520 ms
135,952 KB
testcase_04 WA -
testcase_05 AC 2,417 ms
147,336 KB
testcase_06 AC 2,529 ms
150,708 KB
testcase_07 AC 2,341 ms
141,456 KB
testcase_08 AC 2,353 ms
143,344 KB
testcase_09 AC 2,493 ms
147,428 KB
testcase_10 AC 2,597 ms
149,480 KB
testcase_11 AC 2,489 ms
153,184 KB
testcase_12 AC 2,470 ms
144,852 KB
testcase_13 AC 2,359 ms
158,508 KB
testcase_14 AC 1,815 ms
127,916 KB
testcase_15 AC 2,627 ms
143,228 KB
testcase_16 AC 2,308 ms
149,976 KB
testcase_17 AC 2,549 ms
162,708 KB
testcase_18 AC 2,452 ms
139,832 KB
testcase_19 AC 2,562 ms
154,972 KB
testcase_20 AC 2,629 ms
152,252 KB
testcase_21 AC 2,497 ms
160,536 KB
testcase_22 AC 2,631 ms
140,660 KB
testcase_23 AC 2,151 ms
128,428 KB
testcase_24 AC 2,428 ms
151,236 KB
testcase_25 AC 2,486 ms
144,024 KB
testcase_26 AC 2,655 ms
144,996 KB
testcase_27 AC 2,517 ms
161,324 KB
testcase_28 AC 2,519 ms
148,924 KB
testcase_29 AC 2,510 ms
142,612 KB
testcase_30 AC 2,557 ms
150,252 KB
testcase_31 AC 2,587 ms
144,492 KB
testcase_32 AC 1,823 ms
128,608 KB
testcase_33 AC 2,345 ms
144,620 KB
testcase_34 AC 1,970 ms
135,592 KB
testcase_35 AC 2,321 ms
149,500 KB
testcase_36 AC 2,459 ms
165,356 KB
testcase_37 AC 2,334 ms
162,912 KB
testcase_38 AC 2,371 ms
154,336 KB
testcase_39 AC 2,291 ms
149,340 KB
testcase_40 AC 2,540 ms
151,048 KB
testcase_41 AC 2,494 ms
148,240 KB
testcase_42 AC 2,514 ms
146,276 KB
testcase_43 AC 2,593 ms
146,176 KB
testcase_44 AC 2,323 ms
152,116 KB
testcase_45 AC 2,395 ms
158,848 KB
testcase_46 AC 2,546 ms
159,988 KB
testcase_47 AC 2,445 ms
155,128 KB
testcase_48 AC 1,689 ms
129,084 KB
testcase_49 AC 1,909 ms
122,844 KB
testcase_50 AC 2,588 ms
156,016 KB
testcase_51 AC 2,426 ms
145,404 KB
testcase_52 AC 2,400 ms
148,740 KB
testcase_53 AC 2,141 ms
128,920 KB
testcase_54 AC 2,324 ms
140,772 KB
testcase_55 AC 2,494 ms
147,216 KB
testcase_56 AC 2,487 ms
154,068 KB
testcase_57 AC 2,530 ms
157,904 KB
testcase_58 AC 2,148 ms
145,276 KB
testcase_59 AC 2,383 ms
141,376 KB
testcase_60 AC 2,492 ms
155,640 KB
testcase_61 AC 2,481 ms
139,504 KB
testcase_62 AC 2,434 ms
141,724 KB
testcase_63 AC 2,346 ms
146,736 KB
testcase_64 AC 2,305 ms
147,776 KB
testcase_65 AC 2,551 ms
154,776 KB
testcase_66 AC 2,507 ms
152,172 KB
testcase_67 AC 2,484 ms
157,516 KB
testcase_68 AC 2,497 ms
149,060 KB
testcase_69 AC 2,381 ms
152,976 KB
testcase_70 AC 2,497 ms
157,696 KB
testcase_71 AC 2,501 ms
142,664 KB
testcase_72 AC 2,470 ms
142,512 KB
testcase_73 AC 2,488 ms
151,432 KB
testcase_74 AC 2,317 ms
154,420 KB
testcase_75 AC 2,606 ms
146,180 KB
testcase_76 AC 2,472 ms
152,056 KB
testcase_77 AC 2,374 ms
145,596 KB
testcase_78 AC 2,422 ms
146,704 KB
testcase_79 AC 1,919 ms
121,352 KB
testcase_80 AC 2,486 ms
146,996 KB
testcase_81 AC 2,591 ms
154,236 KB
testcase_82 AC 2,692 ms
140,504 KB
testcase_83 AC 2,499 ms
150,880 KB
testcase_84 AC 2,336 ms
152,012 KB
testcase_85 AC 2,560 ms
143,408 KB
testcase_86 AC 2,388 ms
148,216 KB
testcase_87 AC 2,310 ms
155,052 KB
testcase_88 AC 2,587 ms
151,816 KB
testcase_89 AC 2,392 ms
144,320 KB
testcase_90 AC 2,421 ms
143,164 KB
testcase_91 AC 2,441 ms
144,048 KB
testcase_92 WA -
testcase_93 AC 2,470 ms
150,920 KB
testcase_94 AC 2,450 ms
149,016 KB
testcase_95 AC 2,520 ms
155,012 KB
testcase_96 AC 2,539 ms
154,296 KB
testcase_97 AC 2,509 ms
148,092 KB
testcase_98 AC 2,502 ms
133,268 KB
testcase_99 AC 2,248 ms
150,128 KB
権限があれば一括ダウンロードができます
コンパイルメッセージ
warning: unused variable: `score`
   --> Main.rs:278:9
    |
278 |     let score=beam.best_score.saturating_sub(2);
    |         ^^^^^ help: if this is intentional, prefix it with an underscore: `_score`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: function `simulate` is never used
   --> Main.rs:287:4
    |
287 | fn simulate(input:&In,route:&[usize]){
    |    ^^^^^^^^
    |
    = note: `#[warn(dead_code)]` on by default

warning: field `goal` is never read
   --> Main.rs:449:5
    |
443 | struct In{
    |        -- field in this struct
...
449 |     goal:usize,
    |     ^^^^

warning: 3 warnings emitted

ソースコード

diff #

#![allow(non_snake_case)]
#[cfg(not(local))]macro_rules! eprintln{($($_:tt)*)=>{}}


const MAX_TURN:usize=40; // todo

struct BUF{
    base:i64,
    dist:Vec<i64>,
    prev:Vec<Vec<usize>>,
    que:BinaryHeap<Reverse<S<i64,(usize,usize)>>>,
}
impl BUF{
    fn new()->BUF{
        BUF{
            base:i64::MAX,
            dist:vec![i64::MAX;N2],
            prev:vec![vec![!0;MAX_TURN];N2],
            que:BinaryHeap::new(),
        }
    }
    
    fn reset(&mut self){
        self.base-=100000;
        self.que.clear();
    }
}


// todo
// pinch_penalty
fn score(juwels:usize,key:bool)->f64{
    const KEY_BONUS:f64=2.5;
    
    juwels as f64
    +if key{
        KEY_BONUS
    } else {
        0.
    }
}


#[derive(Clone)]
struct Node{
    hist:List<usize>,
    seen:Vec<u8>,
    pos:usize,
    turn:usize,
    HP:i64,
    juwels:usize,
}
impl Node{
    fn new(input:&In)->Node{
        Node{
            hist:Cons(input.start,Rc::new(Nil)),
            seen:vec![0;(input.I>>3)+1],
            pos:input.start,
            turn:0,
            HP:H,
            juwels:0,
        }
    }

    fn get(&self,id:usize)->bool{
        let (a,b)=(id>>3,id&7);
        self.seen[a]>>b&1==1
    }

    fn get_routes(&self,input:&In,BF:&mut BUF)->Vec<(Vec<usize>,i64,Cell)>{
        // todo
        const C:usize=3;
        let R:i64=50.min(self.HP+0);

        let old=BF.base;
        BF.reset();
        let mut ret=vec![];
        BF.que.push(Reverse(S(BF.base,(self.pos,0))));
        BF.dist[self.pos]=BF.base;
        BF.prev[self.pos][0]=!0;
        let th=BF.base+R;

        while let Some(Reverse(S(cost,(pos,t))))=BF.que.pop(){
            let turn=(self.turn+t)%60;
            let nturn=(turn+1)%60;
            let nt=t+1;
            
            for dd in DX{
                let np=pos+dd;
                let cell=input.grid[np];
                let nc=input.damage[np][nturn]+cost;
                if cell==Goal && self.get(0) || matches!(cell,Juwel(id) if !self.get(id)){
                    if dd==0{
                        continue;
                    }
                    let nc=if cell==Goal{cost}else{nc};
                    let mut route=vec![np];
                    let mut pos=(pos,t);
                    loop{
                        route.push(pos.0);
                        pos=(BF.prev[pos.0][pos.1],pos.1-1);
                        if pos.0==!0{
                            break;
                        }
                    }
                    route.pop();
                    route.reverse();
                    ret.push((route,nc-BF.base,cell));
                    if ret.len()>=C{
                        return ret;
                    }
                }
                else if cell.is_through() && MAX_TURN>nt{
                    if old>BF.dist[np] && dd!=0{
                        continue;
                    }
                    BF.dist[np]=old-1;

                    if (BF.dist[np]>nc || dd==0) && th>=nc{
                        BF.dist[np]=BF.dist[np].min(nc);
                        BF.que.push(Reverse(S(nc,(np,nt))));
                        BF.prev[np][nt]=pos;
                    }
                }
            }
        }
        
        ret
    }

    fn to_cand(&self,input:&In,id:(usize,usize),route:Vec<usize>,diff:i64)->Option<Cand>{
        let HP=self.HP-diff;
        
        let pos=*route.last().unwrap();
        let key=(self.seen[0]&1==1)|(input.id[pos]==0);

        let cost=input.cost(pos,key);
        if HP<=0 || cost>=HP as f64+0.{ // todo
            return None;
        }
        let juwels=self.juwels+1;

        Some(
            Cand{
                parent:id,
                HP,
                juwels,pos,
                turn:self.turn+route.len(),
                score:score(juwels,key),
                op:route
            }
        )
    }
    
    fn append_cands(&self,input:&In,id:(usize,usize),cands:&mut Vec<Vec<Cand>>,best_score:&mut usize,best:&mut List<usize>,BF:&mut BUF){
        let routes=self.get_routes(input,BF);
        
        for (route,diff,cell) in routes{
            if let Some(cand)=self.to_cand(input,id,route,diff){
                if cell==Goal{
                    if *best_score<cand.juwels{
                        *best_score=cand.juwels;
                        *best=self.hist.append(&cand.op);
                    }
                }
                else{
                    cands[cand.HP as usize].push(cand);
                }
            }
        }
    }
}


#[derive(Clone)]
struct Cand{
    parent:(usize,usize),
    op:Vec<usize>,
    pos:usize,
    turn:usize,
    HP:i64,
    juwels:usize,
    score:f64,
}
impl Cand{
    fn to_node(&self,input:&In,parent:&Node)->Node{
        let mut seen=parent.seen.clone();
        if let Juwel(id)=input.grid[self.pos]{
            seen[id>>3]|=1<<(id&7);
        }

        Node{
            seen,
            hist:parent.hist.append(&self.op),
            pos:self.pos,
            turn:self.turn,
            HP:self.HP,
            juwels:self.juwels,
        }
    }
}


struct Beam{
    nodes:Vec<Vec<Node>>,
    best_score:usize,
    best:List<usize>
}
impl Beam{
    fn new(node:Node)->Beam{
        let mut nodes=vec![vec![];H as usize+1];
        nodes[H as usize].push(node);
        Beam{
            nodes,
            best_score:0,
            best:Nil
        }
    }

    fn enum_cands(&mut self,input:&In,HP:usize,cands:&mut Vec<Vec<Cand>>,BF:&mut BUF){
        for (i,node) in self.nodes[HP].iter().enumerate(){
            node.append_cands(input,(HP,i),cands,&mut self.best_score,&mut self.best,BF);
        }
    }

    fn update<'a,I:Iterator<Item=&'a Cand>>(&mut self,input:&In,HP:usize,it:I){
        assert!(self.nodes[HP].is_empty());
        for cand in it{
            let (i,j)=cand.parent;
            let node=cand.to_node(input,&self.nodes[i][j]);
            self.nodes[HP].push(node);
        }
    }
}


fn solve(input:&In)->Vec<usize>{
    let mut M=120;
    
    let mut BF=BUF::new();
    let mut beam=Beam::new(Node::new(input));
    let mut cands=vec![vec![];H as usize+1];

    let mut cnt=vec![0;input.I];

    let mut prev=H as usize;
    let mut time0=get_time();
    
    for i in (1..=H as usize).rev(){
        if H as usize-1000>i && i&15==0 || i==112 || i==80 || i==48 || i==16 || i==8 || i==4 || i==2{
            let time1=get_time();
            if 2.7<=time1{
                M=80;
            }
            
            let mut rate=(prev-i) as f64*(2.7-time1)/i as f64/(time1-time0);
            if rate>=1.{
                rate=rate.cbrt();
            }
            M=((M as f64*rate) as usize).max(90).min((input.I as f64) as usize);
            eprintln!("# {M}");
            prev=i;
            time0=time1;
        }
        
        beam.enum_cands(input,i,&mut cands,&mut BF);
        if i==1{
            break;
        }

        cands[i-1].sort_unstable_by_key(|cand|Reverse(O(cand.score)));
        cnt.fill(0);
        let th=1; // todo
        let it=cands[i-1].iter().filter(|cand|cnt[input.id[cand.pos]]<th && {cnt[input.id[cand.pos]]+=1; true}).take(M);
        beam.update(input,i-1,it);
    }

    let score=beam.best_score.saturating_sub(2);
    // assert!(score!=0);
    eprintln!("score = {}",score);
    let ret=beam.best.to_vec();

    ret
}


fn simulate(input:&In,route:&[usize]){
    let mut HP=H;
    for (i,p) in route.iter().cloned().enumerate(){
        if i==0 || i==route.len()-1{
            continue;
        }
        HP-=input.damage[p][i%60];
    }
    assert!(HP>=0);
    eprintln!("HP = {HP}");
}


fn main(){
    get_time();
    let input=In::input();
    eprintln!("I = {}",input.I);
    let ans=solve(&input);

    #[cfg(local)]{
        simulate(&input,&ans);
    }

    for w in ans.windows(2){
        let id=(0..5).find(|i|DX[*i]==w[1]-w[0]).unwrap();
        println!("{}",DC[id]);
    }

    eprintln!("time = {:.2}",get_time());
}


///////////////////////////////////


#[allow(unused)]
struct Scanner{
    stack:std::str::SplitAsciiWhitespace<'static>
}
#[allow(unused)]
impl Scanner{
    fn new()->Self{
        use std::io::Read;
        let mut tmp=String::new();
        std::io::stdin().read_to_string(&mut tmp).unwrap();
        Self{stack:Box::leak(tmp.into_boxed_str()).split_ascii_whitespace()}
    }

    fn read<T:std::str::FromStr>(&mut self)->T{
        self.stack.next().unwrap().parse::<T>().unwrap_or_else(|_|panic!("parse error {}",std::any::type_name::<T>()))
    }
}


macro_rules! input{
    ($scan:ident, $($rest:tt)*)=>{
        input_inner!{$scan,$($rest)*}
    };
}
 
macro_rules! input_inner{
    ($scan:ident $(,)?)=>{};
    ($scan:ident, $name:ident:$t:tt $($rest:tt)*)=>{
        let $name=read_value!($scan,$t);
        input_inner!{$scan $($rest)*}
    };
}
 
macro_rules! read_value{
    ($scan:ident, ($($t:tt),*))=>{
        ($(read_value!($scan, $t)),*)
    };
    ($scan:ident, [$t:tt;$len:expr])=>{
        (0..$len).map(|_|read_value!($scan,$t)).collect::<Vec<_>>()
    };
    ($scan:ident, Chars)=>{
        read_value!($scan,String).chars().collect::<Vec<char>>()
    };
    ($scan:ident, Usize1)=>{
        read_value!($scan,usize)-1
    };
    ($scan:ident, $t:ty)=>{
        $scan.read::<$t>()
    };
}

use std::collections::*;
use std::cmp::Reverse;
use std::f64::INFINITY;


#[derive(Clone,Copy,PartialEq,Eq,Debug)]
enum Cell{
    Empty,
    Wall,
    Goal,
    Juwel(usize),
    Fire,
    Enemy
}
use Cell::*;
impl Cell{
    fn new(c:char,at:&mut usize)->Cell{
        match c{
            '.'|'S'=>Empty,
            '#'=>Wall,
            'G'=>Goal,
            'K'=>Juwel(0),
            'J'=>{*at+=1; Juwel(*at)},
            'F'=>Fire,
            'E'=>Enemy,
            _=>panic!(),
        }
    }

    fn is_through(&self)->bool{
        !matches!(*self,Wall|Goal|Enemy)
    }

    fn is_block(&self)->bool{
        matches!(self,Wall|Enemy)
    }
}

const N:usize=60;
const N2:usize=(N+2).pow(2);
const H:i64=1500;


fn get_cost(grid:&[Cell],costs:&[f64],start:usize)->Vec<f64>{
    let mut que=BinaryHeap::new();
    que.push(Reverse(S(0.,start)));
    let mut ret=vec![INFINITY;N2];
    ret[start]=0.;

    while let Some(Reverse(S(cost,pos)))=que.pop(){
        for dd in DD{
            let np=pos+dd;
            if ret[np].is_infinite(){
                let nc=if grid[np].is_through(){
                    cost+costs[np]
                }
                else{
                    continue;
                };

                ret[np]=nc;
                que.push(Reverse(S(nc,np)));
            }
        }
    }

    ret
}


struct In{
    I:usize,
    grid:Vec<Cell>,
    id:Vec<usize>,
    start:usize,
    key:usize,
    goal:usize,
    cost_key:Vec<f64>,
    cost_goal:Vec<f64>,
    damage:Vec<Vec<i64>>,
}
impl In{
    fn input()->In{
        let mut scan=Scanner::new();
        input!{
            scan,
            n:usize,
            D:i64,
            h:i64,
            igrid:[Chars;n],
            M:usize,
            ienemy:[(usize,usize,usize);M]
        }

        assert_eq!((n,h),(N,H));
        let mut grid=vec![Wall;N2];
        let mut at=0;
        let mut start=!0;
        let mut key=!0;
        let mut goal=!0;
        
        for i in 1..=N{
            for j in 1..=N{
                let pos=to(i,j);
                let c=igrid[i-1][j-1];
                grid[pos]=Cell::new(c,&mut at);
                if c=='S'{
                    start=pos;
                }
                else if c=='K'{
                    key=pos;
                }
                else if c=='G'{
                    goal=pos;
                }
            }
        }
        assert!(start!=!0 && key!=!0 && goal!=!0);

        let mut enemy=vec![!0;M];
        let mut costf=vec![0.;N2];
        let mut reach=vec![vec![];N2];
        for (id,&(i,j,d)) in ienemy.iter().enumerate(){
            enemy[id]=d;

            let pos=to(i+1,j+1);
            let addf=D as f64/d as f64+1.;
            for dd in DD{
                let mut np=pos+dd;
                while !grid[np].is_block(){
                    costf[np]+=addf;
                    reach[np].push(id);
                    np+=dd;
                }
            }
        }

        let mut damage=vec![vec![0;60];N2];
        for i in 1..=N{
            for j in 1..=N{
                for k in 0..60{
                    let cost=reach[to(i,j)].iter().map(|id|(k%enemy[*id]==0) as i64).sum::<i64>()*D+1;
                    damage[to(i,j)][k]=cost;
                }
            }
        }

        let mut I=0;
        let mut id=vec![!0;N2];
        for i in 1..=N{
            for j in 1..=N{
                if let Juwel(idx)=grid[to(i,j)]{
                    id[to(i,j)]=idx;
                    I+=1;
                }
            }
        }

        In{
            cost_key:get_cost(&grid,&costf,key),
            cost_goal:get_cost(&grid,&costf,goal),
            grid,start,key,goal,
            damage,I,id,
        }
    }

    fn cost(&self,pos:usize,key:bool)->f64{
        if key{
            self.cost_goal[pos]
        }
        else{
            self.cost_goal[self.key]+self.cost_key[pos]
        }
    }
}


fn to(i:usize,j:usize)->usize{
    i*(N+2)+j
}

const DD:[usize;4]=[!0,(N+2).wrapping_neg(),1,N+2];
const DX:[usize;5]=[!0,(N+2).wrapping_neg(),1,N+2,0];
const DC:[&'static str;5]=["M L","M U","M R","M D","S"];


#[derive(PartialEq,PartialOrd)]
struct O<T:PartialEq+PartialOrd>(T);
impl<T:PartialEq+PartialOrd> Eq for O<T>{} 
impl<T:PartialEq+PartialOrd> Ord for O<T>{
    fn cmp(&self,a:&O<T>)->std::cmp::Ordering{
        self.0.partial_cmp(&a.0).unwrap()
    }
}

struct S<T:PartialEq+PartialOrd,U>(T,U);
impl<T:PartialEq+PartialOrd,U> PartialEq for S<T,U>{
    fn eq(&self,a:&S<T,U>)->bool{
        self.0.eq(&a.0)
    }
}
impl<T:PartialEq+PartialOrd,U> PartialOrd for S<T,U>{
    fn partial_cmp(&self,a:&S<T,U>)->Option<std::cmp::Ordering>{
        self.0.partial_cmp(&a.0)
    }
}
impl<T:PartialEq+PartialOrd,U> Eq for S<T,U>{}
impl<T:PartialEq+PartialOrd,U> Ord for S<T,U>{
    fn cmp(&self,a:&S<T,U>)->std::cmp::Ordering{
        self.partial_cmp(a).unwrap()
    }
}


use std::rc::Rc;
#[derive(Clone)]
enum List<T>{
    Cons(T,Rc<List<T>>),
    Nil
}
use List::*;
impl<T:Clone> List<T>{
    fn to_vec(&self)->Vec<T>{
        let mut ret=vec![];
        let mut ptr=self.clone();

        while let Cons(v,prev)=ptr{
            ret.push(v.clone());
            ptr=(*prev).clone();
        }

        ret.reverse();
        ret
    }

    fn append(&self,new:&[T])->List<T>{
        let mut ret=self.clone();
        for n in new{
            ret=Cons(n.clone(),Rc::new(ret));
        }
        ret
    }
}


fn get_time()->f64{
    static mut START:f64=-1.;
    let time=std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs_f64();
    unsafe{
        if START<0.{
            START=time;
        }

        #[cfg(local)]{
            (time-START)*1.5
        }
        #[cfg(not(local))]{
            time-START
        }
    }
}
0