結果

問題 No.5017 Tool-assisted Shooting
ユーザー terry_u16terry_u16
提出日時 2023-07-16 18:11:34
言語 Rust
(1.77.0 + proconio)
結果
AC  
実行時間 1,755 ms / 2,000 ms
コード長 9,904 bytes
コンパイル時間 1,909 ms
コンパイル使用メモリ 163,780 KB
実行使用メモリ 28,352 KB
スコア 4,640,478
平均クエリ数 990.00
最終ジャッジ日時 2023-07-16 18:14:18
合計ジャッジ時間 161,845 ms
ジャッジサーバーID
(参考情報)
judge14 / judge11
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1,556 ms
25,408 KB
testcase_01 AC 1,521 ms
25,116 KB
testcase_02 AC 1,447 ms
24,788 KB
testcase_03 AC 1,550 ms
25,048 KB
testcase_04 AC 1,473 ms
25,884 KB
testcase_05 AC 1,469 ms
24,492 KB
testcase_06 AC 1,520 ms
25,124 KB
testcase_07 AC 1,531 ms
25,080 KB
testcase_08 AC 1,466 ms
26,064 KB
testcase_09 AC 1,469 ms
25,020 KB
testcase_10 AC 1,634 ms
25,084 KB
testcase_11 AC 1,491 ms
25,948 KB
testcase_12 AC 1,516 ms
25,536 KB
testcase_13 AC 1,507 ms
25,052 KB
testcase_14 AC 1,500 ms
25,036 KB
testcase_15 AC 1,527 ms
24,496 KB
testcase_16 AC 1,513 ms
25,120 KB
testcase_17 AC 1,514 ms
25,740 KB
testcase_18 AC 1,486 ms
24,580 KB
testcase_19 AC 1,525 ms
24,244 KB
testcase_20 AC 1,504 ms
25,176 KB
testcase_21 AC 1,595 ms
25,928 KB
testcase_22 AC 1,598 ms
25,368 KB
testcase_23 AC 1,467 ms
25,452 KB
testcase_24 AC 1,527 ms
26,208 KB
testcase_25 AC 1,520 ms
24,840 KB
testcase_26 AC 1,482 ms
25,328 KB
testcase_27 AC 1,506 ms
24,908 KB
testcase_28 AC 1,530 ms
25,812 KB
testcase_29 AC 1,496 ms
25,628 KB
testcase_30 AC 1,510 ms
24,472 KB
testcase_31 AC 1,545 ms
25,316 KB
testcase_32 AC 1,525 ms
25,244 KB
testcase_33 AC 1,493 ms
25,168 KB
testcase_34 AC 1,473 ms
25,984 KB
testcase_35 AC 1,515 ms
24,896 KB
testcase_36 AC 1,502 ms
24,608 KB
testcase_37 AC 1,543 ms
25,728 KB
testcase_38 AC 1,502 ms
24,528 KB
testcase_39 AC 1,494 ms
25,036 KB
testcase_40 AC 1,517 ms
25,504 KB
testcase_41 AC 1,518 ms
25,172 KB
testcase_42 AC 1,526 ms
25,016 KB
testcase_43 AC 1,517 ms
24,072 KB
testcase_44 AC 1,503 ms
25,112 KB
testcase_45 AC 1,591 ms
26,192 KB
testcase_46 AC 1,531 ms
25,544 KB
testcase_47 AC 1,479 ms
25,580 KB
testcase_48 AC 1,521 ms
25,604 KB
testcase_49 AC 1,543 ms
25,572 KB
testcase_50 AC 1,513 ms
25,388 KB
testcase_51 AC 1,508 ms
25,016 KB
testcase_52 AC 1,550 ms
24,436 KB
testcase_53 AC 1,652 ms
25,584 KB
testcase_54 AC 1,504 ms
25,184 KB
testcase_55 AC 1,509 ms
25,340 KB
testcase_56 AC 1,510 ms
25,444 KB
testcase_57 AC 1,491 ms
25,448 KB
testcase_58 AC 1,503 ms
25,012 KB
testcase_59 AC 1,561 ms
25,184 KB
testcase_60 AC 1,524 ms
26,064 KB
testcase_61 AC 1,496 ms
25,048 KB
testcase_62 AC 1,526 ms
25,232 KB
testcase_63 AC 1,480 ms
25,256 KB
testcase_64 AC 1,524 ms
24,972 KB
testcase_65 AC 1,529 ms
25,228 KB
testcase_66 AC 1,533 ms
25,256 KB
testcase_67 AC 1,491 ms
25,288 KB
testcase_68 AC 1,755 ms
28,352 KB
testcase_69 AC 1,521 ms
24,908 KB
testcase_70 AC 1,493 ms
24,768 KB
testcase_71 AC 1,524 ms
25,036 KB
testcase_72 AC 1,494 ms
24,404 KB
testcase_73 AC 1,522 ms
25,124 KB
testcase_74 AC 1,518 ms
25,204 KB
testcase_75 AC 1,644 ms
25,120 KB
testcase_76 AC 1,547 ms
26,160 KB
testcase_77 AC 1,552 ms
24,480 KB
testcase_78 AC 1,515 ms
25,332 KB
testcase_79 AC 1,609 ms
25,148 KB
testcase_80 AC 1,629 ms
25,236 KB
testcase_81 AC 1,507 ms
25,504 KB
testcase_82 AC 1,532 ms
24,404 KB
testcase_83 AC 1,619 ms
25,216 KB
testcase_84 AC 1,530 ms
25,512 KB
testcase_85 AC 1,530 ms
25,348 KB
testcase_86 AC 1,506 ms
25,460 KB
testcase_87 AC 1,512 ms
25,100 KB
testcase_88 AC 1,491 ms
24,248 KB
testcase_89 AC 1,515 ms
25,076 KB
testcase_90 AC 1,516 ms
25,892 KB
testcase_91 AC 1,633 ms
25,076 KB
testcase_92 AC 1,467 ms
26,236 KB
testcase_93 AC 1,516 ms
24,928 KB
testcase_94 AC 1,479 ms
25,624 KB
testcase_95 AC 1,561 ms
26,172 KB
testcase_96 AC 1,525 ms
25,652 KB
testcase_97 AC 1,533 ms
25,380 KB
testcase_98 AC 1,528 ms
25,600 KB
testcase_99 AC 1,496 ms
25,252 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

use std::{io::Write, time::Instant};

macro_rules! get {
      ($t:ty) => {
          {
              let mut line: String = String::new();
              std::io::stdin().read_line(&mut line).unwrap();
              line.trim().parse::<$t>().unwrap()
          }
      };
      ($($t:ty),*) => {
          {
              let mut line: String = String::new();
              std::io::stdin().read_line(&mut line).unwrap();
              let mut iter = line.split_whitespace();
              (
                  $(iter.next().unwrap().parse::<$t>().unwrap(),)*
              )
          }
      };
      ($t:ty; $n:expr) => {
          (0..$n).map(|_|
              get!($t)
          ).collect::<Vec<_>>()
      };
      ($($t:ty),*; $n:expr) => {
          (0..$n).map(|_|
              get!($($t),*)
          ).collect::<Vec<_>>()
      };
      ($t:ty ;;) => {
          {
              let mut line: String = String::new();
              std::io::stdin().read_line(&mut line).unwrap();
              line.split_whitespace()
                  .map(|t| t.parse::<$t>().unwrap())
                  .collect::<Vec<_>>()
          }
      };
      ($t:ty ;; $n:expr) => {
          (0..$n).map(|_| get!($t ;;)).collect::<Vec<_>>()
      };
}

pub trait ChangeMinMax {
    fn change_min(&mut self, v: Self) -> bool;
    fn change_max(&mut self, v: Self) -> bool;
}

impl<T: PartialOrd> ChangeMinMax for T {
    fn change_min(&mut self, v: T) -> bool {
        *self > v && {
            *self = v;
            true
        }
    }

    fn change_max(&mut self, v: T) -> bool {
        *self < v && {
            *self = v;
            true
        }
    }
}

const MAX_TURN: usize = 1000;
const DEFAULT_SIMULATION_LEN: usize = 30;
const HEIGHT: usize = 60;
const WIDTH: usize = 25;
const CENTER: usize = 12;
const L: usize = !0;
const C: usize = 0;
const R: usize = 1;
const BEAM_WIDTH: usize = 8;

#[derive(Debug, Clone)]
struct State {
    column: usize,
    power: u32,
    raw_score: u32,
    score: f64,
    turn: usize,
    enemies: EnemyState,
}

impl State {
    fn new() -> Self {
        Self {
            column: CENTER,
            power: 100,
            raw_score: 0,
            turn: 0,
            score: 0.0,
            enemies: EnemyState::new(),
        }
    }

    fn level(&self) -> u32 {
        self.power / 100
    }

    fn move_player(&mut self, direction: usize) {
        self.column = (self.column + direction + WIDTH) % WIDTH;
    }

    fn attack(&mut self, enemy_collection: &EnemyCollection) {
        let level = self.level();

        if self.enemies.has_enemy(enemy_collection, self.column) {
            let (hp, power) = self.enemies.damage(enemy_collection, self.column, level);
            self.raw_score += hp;
            self.power += power;
        }
    }

    fn clean_up(&mut self, enemy_collection: &EnemyCollection) {
        self.enemies.clean_up_enemies(enemy_collection, self.turn);
    }

    fn progress_turn(&mut self, enemy_collection: &EnemyCollection, direction: usize) -> bool {
        let mut alive = true;
        alive &= !self.enemies.crash(enemy_collection, self.column, self.turn);
        self.move_player(direction);
        alive &= !self.enemies.crash(enemy_collection, self.column, self.turn);
        self.attack(enemy_collection);
        self.turn += 1;

        self.update_score(enemy_collection);

        alive
    }

    fn update_score(&mut self, enemy_collection: &EnemyCollection) {
        let mut raw_score_point = self.raw_score as f64;
        let mut power_point = self.power as f64;
        let cols = [
            ((self.column + WIDTH - L) % WIDTH, 0.5),
            (self.column, 1.0),
            ((self.column + R) % WIDTH, 0.5),
        ];

        for &(col, coef) in &cols {
            if let Some(enemy) = self.enemies.get(enemy_collection, col) {
                let ratio = self.enemies.damages[col] as f64 / enemy.hp as f64;
                let coef = coef * ratio * ratio * 0.5;
                raw_score_point += enemy.hp as f64 * coef;
                power_point += enemy.power as f64 * coef;
            }
        }

        let raw_score_coef = (self.turn * self.turn) as f64;
        let power_point_coef = ((MAX_TURN - self.turn) * MAX_TURN) as f64;
        self.score = raw_score_point * raw_score_coef + power_point * power_point_coef;
    }
}

#[derive(Debug, Clone, Copy, Default)]
struct Enemy {
    hp: u32,
    power: u32,
    spawn_turn: usize,
}

impl Enemy {
    fn new(hp: u32, power: u32, spawn_turn: usize) -> Self {
        Self {
            hp,
            power,
            spawn_turn,
        }
    }

    fn is_out_of_range(&self, turn: usize) -> bool {
        self.spawn_turn + HEIGHT <= turn
    }

    fn is_bottom(&self, turn: usize) -> bool {
        self.spawn_turn + HEIGHT - 1 == turn
    }
}

#[derive(Debug, Clone)]
struct EnemyState {
    indices: [usize; WIDTH],
    damages: [u32; WIDTH],
}

impl EnemyState {
    fn new() -> Self {
        Self {
            indices: [0; WIDTH],
            damages: [0; WIDTH],
        }
    }

    fn has_enemy(&self, enemies: &EnemyCollection, column: usize) -> bool {
        self.get(enemies, column).is_some()
    }

    fn get<'a>(&self, enemies: &'a EnemyCollection, column: usize) -> Option<&'a Enemy> {
        enemies.get(column, self.indices[column])
    }

    fn crash(&self, enemies: &EnemyCollection, column: usize, turn: usize) -> bool {
        if let Some(enemy) = enemies.get(column, self.indices[column]) {
            enemy.is_bottom(turn)
        } else {
            false
        }
    }

    fn damage(&mut self, enemies: &EnemyCollection, column: usize, attack: u32) -> (u32, u32) {
        let enemy = enemies.get(column, self.indices[column]).unwrap();
        self.damages[column] += attack;

        if self.damages[column] >= enemy.hp {
            self.damages[column] = 0;
            self.indices[column] += 1;
            (enemy.hp, enemy.power)
        } else {
            (0, 0)
        }
    }

    fn clean_up_enemies(&mut self, enemies: &EnemyCollection, turn: usize) {
        for (column, (index, damage)) in self
            .indices
            .iter_mut()
            .zip(self.damages.iter_mut())
            .enumerate()
        {
            if let Some(enemy) = enemies.get(column, *index) {
                if enemy.is_out_of_range(turn) {
                    *damage = 0;
                    *index += 1;
                }
            }
        }
    }
}

#[derive(Debug, Clone)]
struct EnemyCollection {
    enemies: Vec<Vec<Enemy>>,
}

impl EnemyCollection {
    fn new() -> Self {
        Self {
            enemies: vec![vec![]; WIDTH],
        }
    }

    fn spawn(&mut self, enemies: &[(u32, u32, usize)], turn: usize) {
        for &(hp, power, col) in enemies {
            self.enemies[col].push(Enemy::new(hp, power, turn));
        }
    }

    fn get(&self, column: usize, index: usize) -> Option<&Enemy> {
        self.enemies[column].get(index)
    }
}

fn main() {
    let since = Instant::now();
    let mut state = State::new();
    let mut enemy_collection = EnemyCollection::new();
    let mut turn = 0;

    while let Some(enemies) = read_spawns() {
        enemy_collection.spawn(&enemies, turn);
        let mut all_states = vec![(state.clone(), C)];
        let mut current_states = vec![vec![]; WIDTH];
        current_states[state.column].push(0);
        let simulation_len = DEFAULT_SIMULATION_LEN.min(MAX_TURN - turn);

        for iter in 0..simulation_len {
            let mut next_states = vec![vec![]; WIDTH];

            for &i in current_states.iter().flatten() {
                all_states[i].0.clean_up(&enemy_collection);

                for &dir in &[L, C, R] {
                    let (state, first_dir) = &all_states[i];
                    let mut state = state.clone();
                    let is_alive = state.progress_turn(&enemy_collection, dir);

                    if !is_alive {
                        continue;
                    }

                    let next_col = state.column;
                    let dir = if iter == 0 { dir } else { *first_dir };

                    next_states[next_col].push(all_states.len());
                    all_states.push((state, dir));
                }
            }

            for next in next_states.iter_mut() {
                if next.len() > BEAM_WIDTH {
                    next.select_nth_unstable_by(BEAM_WIDTH, |&i, &j| {
                        all_states[j]
                            .0
                            .score
                            .partial_cmp(&all_states[i].0.score)
                            .unwrap()
                    });
                    next.truncate(BEAM_WIDTH);
                }
            }

            current_states = next_states;
        }

        let mut best_score = std::f64::MIN;
        let mut best_dir = C;

        for (state, dir) in current_states.iter().flatten().map(|&i| &all_states[i]) {
            if best_score.change_max(state.score) {
                best_dir = *dir;
            }
        }

        write_direction(best_dir);
        state.clean_up(&enemy_collection);
        state.progress_turn(&enemy_collection, best_dir);
        turn += 1;

        if turn == MAX_TURN {
            break;
        }
    }

    eprintln!("final score: {}", state.raw_score);
    eprintln!("{:.3}s", (Instant::now() - since).as_secs_f64());
}

fn read_spawns() -> Option<Vec<(u32, u32, usize)>> {
    let n = get!(i32);

    if n < 0 {
        return None;
    }

    let mut enemies = vec![];

    for _ in 0..n {
        enemies.push(get!(u32, u32, usize));
    }

    Some(enemies)
}

fn write_direction(direction: usize) {
    match direction {
        L => println!("L"),
        C => println!("S"),
        R => println!("R"),
        _ => unreachable!(),
    }

    std::io::stdout().flush().unwrap();
}
0