結果
問題 | No.5017 Tool-assisted Shooting |
ユーザー | terry_u16 |
提出日時 | 2023-07-16 18:16:41 |
言語 | Rust (1.77.0 + proconio) |
結果 |
AC
|
実行時間 | 1,835 ms / 2,000 ms |
コード長 | 9,904 bytes |
コンパイル時間 | 1,323 ms |
コンパイル使用メモリ | 163,868 KB |
実行使用メモリ | 26,428 KB |
スコア | 4,662,426 |
平均クエリ数 | 980.00 |
最終ジャッジ日時 | 2023-07-16 18:19:46 |
合計ジャッジ時間 | 176,890 ms |
ジャッジサーバーID (参考情報) |
judge11 / judge12 |
純コード判定しない問題か言語 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 1,696 ms
25,988 KB |
testcase_01 | AC | 1,686 ms
25,524 KB |
testcase_02 | AC | 1,676 ms
25,560 KB |
testcase_03 | AC | 1,630 ms
25,112 KB |
testcase_04 | AC | 1,669 ms
25,608 KB |
testcase_05 | AC | 1,757 ms
25,988 KB |
testcase_06 | AC | 1,665 ms
26,024 KB |
testcase_07 | AC | 1,680 ms
25,900 KB |
testcase_08 | AC | 1,731 ms
25,324 KB |
testcase_09 | AC | 1,613 ms
25,884 KB |
testcase_10 | AC | 1,686 ms
25,760 KB |
testcase_11 | AC | 1,651 ms
25,600 KB |
testcase_12 | AC | 1,670 ms
26,116 KB |
testcase_13 | AC | 1,686 ms
26,428 KB |
testcase_14 | AC | 1,673 ms
25,268 KB |
testcase_15 | AC | 1,666 ms
25,568 KB |
testcase_16 | AC | 1,600 ms
25,288 KB |
testcase_17 | AC | 1,693 ms
25,408 KB |
testcase_18 | AC | 1,673 ms
25,544 KB |
testcase_19 | AC | 1,699 ms
25,296 KB |
testcase_20 | AC | 1,636 ms
25,332 KB |
testcase_21 | AC | 1,658 ms
25,104 KB |
testcase_22 | AC | 1,592 ms
25,268 KB |
testcase_23 | AC | 1,683 ms
25,808 KB |
testcase_24 | AC | 1,716 ms
25,140 KB |
testcase_25 | AC | 1,666 ms
25,768 KB |
testcase_26 | AC | 1,741 ms
25,896 KB |
testcase_27 | AC | 1,668 ms
24,848 KB |
testcase_28 | AC | 1,703 ms
25,024 KB |
testcase_29 | AC | 1,621 ms
25,796 KB |
testcase_30 | AC | 1,573 ms
24,668 KB |
testcase_31 | AC | 1,633 ms
24,964 KB |
testcase_32 | AC | 1,646 ms
25,964 KB |
testcase_33 | AC | 1,692 ms
24,964 KB |
testcase_34 | AC | 1,739 ms
25,608 KB |
testcase_35 | AC | 1,688 ms
26,072 KB |
testcase_36 | AC | 1,665 ms
25,404 KB |
testcase_37 | AC | 1,668 ms
25,388 KB |
testcase_38 | AC | 1,642 ms
25,564 KB |
testcase_39 | AC | 1,751 ms
25,860 KB |
testcase_40 | AC | 1,632 ms
26,256 KB |
testcase_41 | AC | 1,631 ms
25,936 KB |
testcase_42 | AC | 1,621 ms
26,164 KB |
testcase_43 | AC | 1,656 ms
26,220 KB |
testcase_44 | AC | 1,642 ms
25,560 KB |
testcase_45 | AC | 1,661 ms
25,492 KB |
testcase_46 | AC | 1,681 ms
24,812 KB |
testcase_47 | AC | 1,666 ms
25,116 KB |
testcase_48 | AC | 1,676 ms
25,496 KB |
testcase_49 | AC | 1,677 ms
24,380 KB |
testcase_50 | AC | 1,598 ms
24,900 KB |
testcase_51 | AC | 1,621 ms
25,404 KB |
testcase_52 | AC | 1,698 ms
25,456 KB |
testcase_53 | AC | 1,736 ms
26,008 KB |
testcase_54 | AC | 1,686 ms
25,584 KB |
testcase_55 | AC | 1,644 ms
25,468 KB |
testcase_56 | AC | 1,635 ms
25,112 KB |
testcase_57 | AC | 1,626 ms
24,568 KB |
testcase_58 | AC | 1,701 ms
26,040 KB |
testcase_59 | AC | 1,800 ms
26,192 KB |
testcase_60 | AC | 1,664 ms
25,596 KB |
testcase_61 | AC | 1,661 ms
25,840 KB |
testcase_62 | AC | 1,621 ms
25,952 KB |
testcase_63 | AC | 1,714 ms
25,756 KB |
testcase_64 | AC | 1,784 ms
24,320 KB |
testcase_65 | AC | 1,648 ms
25,268 KB |
testcase_66 | AC | 1,693 ms
25,552 KB |
testcase_67 | AC | 1,638 ms
25,840 KB |
testcase_68 | AC | 1,835 ms
26,128 KB |
testcase_69 | AC | 1,615 ms
25,600 KB |
testcase_70 | AC | 1,634 ms
25,456 KB |
testcase_71 | AC | 1,661 ms
25,712 KB |
testcase_72 | AC | 1,639 ms
25,516 KB |
testcase_73 | AC | 1,698 ms
25,888 KB |
testcase_74 | AC | 1,643 ms
25,572 KB |
testcase_75 | AC | 1,757 ms
25,712 KB |
testcase_76 | AC | 1,670 ms
25,904 KB |
testcase_77 | AC | 1,690 ms
25,660 KB |
testcase_78 | AC | 1,752 ms
26,084 KB |
testcase_79 | AC | 1,644 ms
25,104 KB |
testcase_80 | AC | 1,661 ms
25,484 KB |
testcase_81 | AC | 1,686 ms
25,628 KB |
testcase_82 | AC | 1,631 ms
25,860 KB |
testcase_83 | AC | 1,644 ms
26,236 KB |
testcase_84 | AC | 1,650 ms
25,980 KB |
testcase_85 | AC | 1,666 ms
25,704 KB |
testcase_86 | AC | 1,689 ms
24,824 KB |
testcase_87 | AC | 1,608 ms
24,920 KB |
testcase_88 | AC | 1,694 ms
24,248 KB |
testcase_89 | AC | 1,673 ms
25,904 KB |
testcase_90 | AC | 1,687 ms
25,256 KB |
testcase_91 | AC | 1,739 ms
25,272 KB |
testcase_92 | AC | 1,654 ms
25,648 KB |
testcase_93 | AC | 1,729 ms
25,548 KB |
testcase_94 | AC | 1,617 ms
26,096 KB |
testcase_95 | AC | 1,675 ms
25,536 KB |
testcase_96 | AC | 1,786 ms
25,476 KB |
testcase_97 | AC | 1,717 ms
24,212 KB |
testcase_98 | AC | 1,752 ms
26,212 KB |
testcase_99 | AC | 1,627 ms
25,880 KB |
ソースコード
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 = 9; #[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(); }