結果

問題 No.5015 Escape from Labyrinth
ユーザー rhoorhoo
提出日時 2023-04-16 14:57:01
言語 Rust
(1.77.0)
結果
AC  
実行時間 6 ms / 3,000 ms
コード長 19,562 bytes
コンパイル時間 1,901 ms
コンパイル使用メモリ 179,688 KB
実行使用メモリ 4,376 KB
スコア 19,650
最終ジャッジ日時 2023-04-16 14:57:11
合計ジャッジ時間 7,099 ms
ジャッジサーバーID
(参考情報)
judge14 / judge15
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 6 ms
4,372 KB
testcase_01 AC 6 ms
4,368 KB
testcase_02 AC 6 ms
4,368 KB
testcase_03 AC 6 ms
4,372 KB
testcase_04 AC 6 ms
4,372 KB
testcase_05 AC 6 ms
4,372 KB
testcase_06 AC 6 ms
4,376 KB
testcase_07 AC 6 ms
4,376 KB
testcase_08 AC 6 ms
4,372 KB
testcase_09 AC 6 ms
4,368 KB
testcase_10 AC 5 ms
4,372 KB
testcase_11 AC 5 ms
4,372 KB
testcase_12 AC 6 ms
4,368 KB
testcase_13 AC 6 ms
4,368 KB
testcase_14 AC 6 ms
4,368 KB
testcase_15 AC 5 ms
4,372 KB
testcase_16 AC 6 ms
4,368 KB
testcase_17 AC 6 ms
4,368 KB
testcase_18 AC 6 ms
4,368 KB
testcase_19 AC 5 ms
4,368 KB
testcase_20 AC 5 ms
4,376 KB
testcase_21 AC 6 ms
4,372 KB
testcase_22 AC 6 ms
4,372 KB
testcase_23 AC 6 ms
4,368 KB
testcase_24 AC 6 ms
4,372 KB
testcase_25 AC 6 ms
4,376 KB
testcase_26 AC 5 ms
4,368 KB
testcase_27 AC 6 ms
4,368 KB
testcase_28 AC 6 ms
4,368 KB
testcase_29 AC 6 ms
4,372 KB
testcase_30 AC 6 ms
4,372 KB
testcase_31 AC 6 ms
4,368 KB
testcase_32 AC 6 ms
4,368 KB
testcase_33 AC 6 ms
4,368 KB
testcase_34 AC 5 ms
4,372 KB
testcase_35 AC 6 ms
4,372 KB
testcase_36 AC 6 ms
4,368 KB
testcase_37 AC 6 ms
4,372 KB
testcase_38 AC 5 ms
4,372 KB
testcase_39 AC 5 ms
4,372 KB
testcase_40 AC 5 ms
4,368 KB
testcase_41 AC 6 ms
4,368 KB
testcase_42 AC 6 ms
4,372 KB
testcase_43 AC 6 ms
4,372 KB
testcase_44 AC 6 ms
4,372 KB
testcase_45 AC 6 ms
4,368 KB
testcase_46 AC 6 ms
4,372 KB
testcase_47 AC 6 ms
4,372 KB
testcase_48 AC 6 ms
4,372 KB
testcase_49 AC 5 ms
4,368 KB
testcase_50 AC 6 ms
4,368 KB
testcase_51 AC 5 ms
4,368 KB
testcase_52 AC 5 ms
4,372 KB
testcase_53 AC 6 ms
4,368 KB
testcase_54 AC 6 ms
4,368 KB
testcase_55 AC 6 ms
4,372 KB
testcase_56 AC 6 ms
4,368 KB
testcase_57 AC 6 ms
4,372 KB
testcase_58 AC 6 ms
4,376 KB
testcase_59 AC 6 ms
4,368 KB
testcase_60 AC 6 ms
4,372 KB
testcase_61 AC 6 ms
4,372 KB
testcase_62 AC 5 ms
4,372 KB
testcase_63 AC 5 ms
4,372 KB
testcase_64 AC 6 ms
4,372 KB
testcase_65 AC 5 ms
4,368 KB
testcase_66 AC 5 ms
4,368 KB
testcase_67 AC 5 ms
4,372 KB
testcase_68 AC 6 ms
4,368 KB
testcase_69 AC 5 ms
4,372 KB
testcase_70 AC 6 ms
4,368 KB
testcase_71 AC 6 ms
4,368 KB
testcase_72 AC 5 ms
4,376 KB
testcase_73 AC 6 ms
4,372 KB
testcase_74 AC 6 ms
4,368 KB
testcase_75 AC 6 ms
4,372 KB
testcase_76 AC 6 ms
4,368 KB
testcase_77 AC 6 ms
4,372 KB
testcase_78 AC 6 ms
4,372 KB
testcase_79 AC 6 ms
4,372 KB
testcase_80 AC 6 ms
4,368 KB
testcase_81 AC 6 ms
4,372 KB
testcase_82 AC 5 ms
4,368 KB
testcase_83 AC 5 ms
4,372 KB
testcase_84 AC 5 ms
4,368 KB
testcase_85 AC 6 ms
4,372 KB
testcase_86 AC 5 ms
4,368 KB
testcase_87 AC 6 ms
4,368 KB
testcase_88 AC 5 ms
4,368 KB
testcase_89 AC 6 ms
4,372 KB
testcase_90 AC 6 ms
4,368 KB
testcase_91 AC 6 ms
4,372 KB
testcase_92 AC 6 ms
4,372 KB
testcase_93 AC 6 ms
4,368 KB
testcase_94 AC 6 ms
4,368 KB
testcase_95 AC 5 ms
4,372 KB
testcase_96 AC 5 ms
4,372 KB
testcase_97 AC 5 ms
4,372 KB
testcase_98 AC 6 ms
4,368 KB
testcase_99 AC 6 ms
4,368 KB
権限があれば一括ダウンロードができます
コンパイルメッセージ
warning: unused variable: `score0`
   --> Main.rs:400:14
    |
400 |         let (score0,mut route0)=find_path(input.start,input.key);
    |              ^^^^^^ help: if this is intentional, prefix it with an underscore: `_score0`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `score1`
   --> Main.rs:402:14
    |
402 |         let (score1,mut route1)=find_path(input.key,input.goal);
    |              ^^^^^^ help: if this is intentional, prefix it with an underscore: `_score1`

warning: constant `MAX_TURN` is never used
 --> Main.rs:5:7
  |
5 | const MAX_TURN:usize=N*2;
  |       ^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: struct `BUF` is never constructed
 --> Main.rs:7:8
  |
7 | struct BUF{
  |        ^^^

warning: function `score` is never used
  --> Main.rs:34:4
   |
34 | fn score(HP:i64,juwels:usize,key:bool)->f64{
   |    ^^^^^

warning: fields `hist`, `seen`, `pos`, `turn`, `HP`, and `juwels` are never read
  --> Main.rs:50:5
   |
49 | struct Node{
   |        ---- fields in this struct
50 |     hist:List<usize>,
   |     ^^^^
51 |     seen:Vec<u8>,
   |     ^^^^
52 |     pos:usize,
   |     ^^^
53 |     turn:usize,
   |     ^^^^
54 |     HP:i64,
   |     ^^
55 |     juwels:usize,
   |     ^^^^^^
   |
   = note: `Node` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis

warning: multiple fields are never read
   --> Main.rs:245:5
    |
244 | struct Cand{
    |        ---- fields in this struct
245 |     parent:(usize,usize),
    |     ^^^^^^
246 |     op:Vec<usize>,
    |     ^^
247 |     pos:usize,
    |     ^^^
248 |     turn:usize,
    |     ^^^^
249 |     HP:i64,
    |     ^^
250 |     juwels:usize,
    |     ^^^^^^
251 |     score:f64,
    |     ^^^^^
    |
    = note: `Cand` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis

warning: struct `Beam` is never constructed
   --> Main.rs:272:8

ソースコード

diff #

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


const MAX_TURN:usize=N*2;

struct BUF{
    base:i64,
    dist:Vec<i64>,
    prev:Vec<Vec<usize>>,
    que:BinaryHeap<Reverse<S<i64,(usize,usize)>>>,
    que0:BinaryHeap<Reverse<S<(i64,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(),
            que0:BinaryHeap::new()
        }
    }
    
    fn reset(&mut self){
        self.base-=100000;
        self.que.clear();
    }
}


// todo
// pinch_penalty
fn score(HP:i64,juwels:usize,key:bool)->f64{
    const KEY_BONUS0:f64=3.;
    const KEY_BONUS1:f64=30.;
    
    juwels as f64
    +if key{
        let r=(1500-HP) as f64/1500.;
        KEY_BONUS0*(KEY_BONUS1/KEY_BONUS0).powf(r)
    } 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 to_goal(&self,input:&In,best:&mut List<usize>,best_score:&mut usize,BF:&mut BUF){
        assert!(self.get(0));
        
        let old=BF.base;
        BF.reset();
        BF.que0.push(Reverse(S((BF.base,0),(self.pos,0))));
        BF.dist[self.pos]=BF.base;
        BF.prev[self.pos][0]=!0;

        let th=BF.base+self.HP;

        while let Some(Reverse(S((cost,juwels),(pos,t))))=BF.que0.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{
                    let diff=cost-BF.base;
                    if dd==0 || self.HP<=diff{
                        continue;
                    }
                    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();
                    
                    let juwels=self.juwels-juwels as usize;

                    assert!(0<self.HP-diff);
                    if *best_score<juwels{
                        *best_score=juwels;
                        *best=self.hist.append(&route);
                    }

                    return;
                }
                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.que0.push(Reverse(S((nc,juwels-matches!(cell,Juwel(id) if !self.get(id)) as i64),(np,nt))));
                        BF.prev[np][nt]=pos;
                    }
                }
            }
        }
    }

    fn get_routes(&self,input:&In,BF:&mut BUF)->Vec<(Vec<usize>,i64,Cell)>{
        // todo
        const C:usize=3;
        let R:i64=70.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() && 40>nt{ // todo
                    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(HP,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{
                        assert!(cand.HP>0);
                        *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=100;
    
    // 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.6<=time1{
    //             M=70;
    //         }
            
    //         let mut rate=(prev-i) as f64*(2.6-time1)/i as f64/(time1-time0);
    //         if rate>=1.{
    //             rate=rate.cbrt();
    //         }
    //         M=((M as f64*rate) as usize).max(70).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 Beam{mut best_score,mut best,..}=beam;
    // eprintln!("end = {:.2}",get_time());
    
    // 'outer: for i in 0..H as usize{
    //     for node in &beam.nodes[i]{
    //         if node.get(0){
    //             node.to_goal(input,&mut best,&mut best_score,&mut BF);
    //         }
    //         if get_time()>=2.8{
    //             eprintln!("# {i}");
    //             break 'outer;
    //         }
    //     }
    // }

    // // しょうがないから自明解を出力
    // if best_score==0{
        let find_path=|start:usize,goal:usize|->(i64,Vec<usize>){
            let mut prev=vec![!0;N2];
            let mut que=BinaryHeap::new();
            prev[start]=!1;
            que.push(Reverse(S((0,0),start)));
            let mut ans=0;
            'outer: while let Some(Reverse(S((dist,juwels),pos)))=que.pop(){
                for &dd in &DD[..4]{
                    let np=pos+dd;
                    if np==goal{
                        prev[goal]=pos;
                        ans=juwels;
                        break 'outer;
                    }
                    let cell=input.grid[np];
                    if cell.is_through() && prev[np]==!0{
                        prev[np]=pos;
                        que.push(Reverse(S((dist+1,juwels-matches!(cell,Juwel(_)) as i64),np)));
                    }
                }
            }

            let mut route=vec![];
            let mut pos=goal;
            loop{
                route.push(pos);
                pos=prev[pos];
                if pos==!1{
                    break;
                }
            }
            route.pop();
            route.reverse();

            (-ans,route)
        };
        let mut route=vec![input.start];
        let (score0,mut route0)=find_path(input.start,input.key);
        route.append(&mut route0);
        let (score1,mut route1)=find_path(input.key,input.goal);
        route.append(&mut route1);
        eprintln!("score = {}",score0+score1);

        return route;
    // }

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


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