#![allow(dead_code)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] use grid::{Coord, Map2d, DC}; use std::cmp::*; use std::collections::*; use std::io::stdin; use std::process::id; const TIME_LIMIT: f64 = 2.9; const N: usize = 50; const N2: usize = N * N; const M: usize = 20; pub fn main() { get_time(); let input = read_input(); let output = solve(&input); output.print_ans(); eprintln!("Time = {}", get_time()); } fn solve(input: &Input) -> Output { let state = loop { let mut state = State::new(input); state.calc_score_4(input); if state.score < usize::MAX / 3 { break state; } }; let duration = TIME_LIMIT - get_time(); annealing(input, duration, state).to_output_2(input) } #[derive(Clone)] struct State { score: usize, use_bomb: Map2d>, cnt_use: Map2d, jun: Vec<(Coord, usize)>, } impl State { fn new(input: &Input) -> Self { let score = !0; let mut use_bomb = Map2d::new(vec![vec![false; M]; N2], N); let mut cnt_use = Map2d::new(vec![0; N2], N); let mut jun = vec![]; let mut pvec = vec![]; let mut pvec2 = vec![]; for x in 0..N { for y in 0..N { if 5 < x && x < N - 5 && 5 < y && y < N - 5 { pvec.push(Coord::new(x, y)); } else { pvec2.push(Coord::new(x, y)); } } } rnd::shuffle(&mut pvec); rnd::shuffle(&mut pvec2); pvec.extend(pvec2); pvec.reverse(); for p in pvec { if input.map[p] == Square::Empty || cnt_use[p] > 0 { continue; } if let Some(&(bp, bid)) = input.can_hakai_rev[p] .iter() .find(|&&bp| 5 < bp.0.x && bp.0.x < N - 5 && 5 < bp.0.y && bp.0.y < N - 5) { use_bomb[bp][bid] = true; for &np in input.can_hakai[bp][bid].iter() { cnt_use[np] += 1; } jun.push((bp, bid)); } else { let (bp, bid) = input.can_hakai_rev[p][rnd::next() % input.can_hakai_rev[p].len()]; use_bomb[bp][bid] = true; for &np in input.can_hakai[bp][bid].iter() { cnt_use[np] += 1; } jun.push((bp, bid)); } } Self { score, use_bomb, cnt_use, jun, } } fn calc_score_4(&mut self, input: &Input) { let mut score = 0; let sz = self.jun.len(); // uso dp // 前から let max_size = 3; let mut dp = vec![vec![usize::MAX; max_size]; 2]; dp[0][0] = 0; static mut bcnt: i32 = -1; static mut bombed2: [i32; N2] = [-1; N2]; unsafe { bcnt += 1; for i in 0..sz { score += 5 * input.bomb_cost[self.jun[i].1]; // 購入する場合0->j if dp[i % 2][0] != usize::MAX { if let Some(&near) = input.near_shop[self.jun[i].0] .iter() .find(|&&np| bombed2[np.idx(N)] != bcnt) { for j in 0..max_size { let cost = if i == 0 { dp[i % 2][0] + 1 * Coord::new(0, 0).dist(&near) + 1 * (j + 2) * (j + 2) * near.dist(&self.jun[i].0) } else { dp[i % 2][0] + 1 * self.jun[i - 1].0.dist(&near) + 1 * (j + 2) * (j + 2) * near.dist(&self.jun[i].0) }; dp[(i + 1) % 2][j] = dp[(i + 1) % 2][j].min(cost); } } } // 購入しない場合 for j in 1..max_size { if dp[i % 2][j] == usize::MAX { continue; } let cost = dp[i % 2][j] + 1 * (j + 1) * (j + 1) * self.jun[i - 1].0.dist(&self.jun[i].0); dp[(i + 1) % 2][j - 1] = dp[(i + 1) % 2][j - 1].min(cost); } for &np in input.can_hakai_shop[self.jun[i].0][self.jun[i].1].iter() { bombed2[np.idx(N)] = bcnt; } for xxxx in 0..max_size { dp[i % 2][xxxx] = usize::MAX; } } if dp[sz % 2][0] == usize::MAX { self.score = usize::MAX / 2; } else { self.score = score + dp[sz % 2][0]; } } } fn to_output_2(&self, input: &Input) -> Output { let mut actions = vec![]; let sz = self.jun.len(); let max_size = sz + 1; let mut dp = vec![vec![(usize::MAX, usize::MAX); max_size]; sz + 1]; dp[0][0] = (0, !0); let mut bombed = Map2d::new(vec![false; N2], N); for i in 0..sz { // 購入する場合0->j if dp[i][0].0 != usize::MAX { /*if let Some(&near) = input.shops .iter() .find(|&&np| !bombed[np])*/ let mae = if i == 0 { Coord::new(0, 0) } else { self.jun[i - 1].0 }; if let Some(&near) = input .shops .iter() .filter(|&&np| !bombed[np]) .min_by(|&&a, &&b| { let acost = mae.dist(&a) + 4 * a.dist(&self.jun[i].0); let bcost = mae.dist(&b) + 4 * b.dist(&self.jun[i].0); acost.cmp(&bcost) }) { for j in 0..max_size { let cost = if i == 0 { dp[i][0].0 + Coord::new(0, 0).dist(&near) + (j + 2) * (j + 2) * near.dist(&self.jun[i].0) } else { dp[i][0].0 + self.jun[i - 1].0.dist(&near) + (j + 2) * (j + 2) * near.dist(&self.jun[i].0) }; dp[i + 1][j] = dp[i + 1][j].min((cost, 0)); } } } // 購入しない場合 for j in 1..max_size { if dp[i][j].0 == usize::MAX { continue; } let cost = dp[i][j].0 + (j + 1) * (j + 1) * self.jun[i - 1].0.dist(&self.jun[i].0); dp[i + 1][j - 1] = dp[i + 1][j - 1].min((cost, j)); } for &np in input.can_hakai[self.jun[i].0][self.jun[i].1].iter() { bombed[np] = true; } } // 復元 let mut rireki = vec![]; let mut nowj = 0; for i in (1..sz + 1).rev() { // use rireki.push((3, !0)); let nextnowj = dp[i][nowj].1; assert!(nextnowj != usize::MAX); assert!(nowj + 1 >= nextnowj); if nowj >= nextnowj { // buy rireki.push((2, nowj - nextnowj + 1)); } nowj = nextnowj; if nowj == usize::MAX { eprintln!("AAA {} {} {} {}", i, sz, dp[sz][0].0, dp[sz][0].1); } } rireki.reverse(); let mut bombed = Map2d::new(vec![false; N2], N); let mut mae = Coord::new(0, 0); let mut idx = 0; for (tp, d) in rireki { if tp == 2 { // buy let near = *input .shops .iter() .filter(|&&np| !bombed[np]) .min_by(|&&a, &&b| { let acost = mae.dist(&a) + 4 * a.dist(&self.jun[idx].0); let bcost = mae.dist(&b) + 4 * b.dist(&self.jun[idx].0); acost.cmp(&bcost) }) .unwrap(); assert!(input.map[near] == Square::Shop && !bombed[near]); actions.extend(moves_dijkstra(mae, near, &bombed, input)); for j in 0..d { actions.push((2, self.jun[idx + j].1 + 1)); } mae = near; } else { // use actions.extend(moves_dijkstra(mae, self.jun[idx].0, &bombed, input)); mae = self.jun[idx].0; actions.push((3, self.jun[idx].1 + 1)); for &np in input.can_hakai[self.jun[idx].0][self.jun[idx].1].iter() { bombed[np] = true; } idx += 1; } } Output { score: self.score, actions, } } fn add_bomb(&mut self, input: &Input, bp: Coord, bid: usize) { for &np in input.can_hakai[bp][bid].iter() { self.cnt_use[np] += 1; } } fn swap_jun(&mut self, pos: (usize, usize)) { let a = self.jun[pos.0]; self.jun[pos.0] = self.jun[pos.1]; self.jun[pos.1] = a; } fn swap_off2(&mut self, input: &Input) -> (bool, usize, (Coord, usize), Vec<(Coord, usize)>) { let idx = rnd::next() % self.jun.len(); let bp = self.jun[idx].0; let bid = self.jun[idx].1; let mut added = vec![]; let mut is_ng = false; for &np in input.can_hakai[bp][bid].iter() { self.cnt_use[np] -= 1; if self.cnt_use[np] == 0 && !is_ng { let lllen = input.can_hakai_rev[np].len(); let sidx = rnd::next() % lllen; if let Some(lll) = (0..lllen).find(|&iiii| { !self.use_bomb[input.can_hakai_rev[np][(iiii + sidx) % lllen].0] [input.can_hakai_rev[np][(iiii + sidx) % lllen].1] }) { let nb = input.can_hakai_rev[np][(lll + sidx) % lllen]; self.add_bomb(input, nb.0, nb.1); added.push(nb); self.use_bomb[nb.0][nb.1] = true; } else { is_ng = true; } } } if is_ng { return (false, idx, (bp, bid), added); } self.use_bomb[bp][bid] = false; self.jun.remove(idx); for &nb in added.iter() { let mut min_cost = !0; let mut best_idx = !0; for i in 0..self.jun.len() { let dist = if i == 0 { Coord::new(0, 0).dist(&nb.0) + nb.0.dist(&self.jun[i].0) } else { self.jun[i - 1].0.dist(&nb.0) + nb.0.dist(&self.jun[i].0) }; if min_cost > dist { min_cost = dist; best_idx = i; } } self.jun.insert(best_idx, nb); } (true, idx, (bp, bid), added) } fn undo_swap_off( &mut self, input: &Input, rireki: (bool, usize, (Coord, usize), Vec<(Coord, usize)>), ) { self.add_bomb(input, rireki.2 .0, rireki.2 .1); if !rireki.0 { return; } for &nb in rireki.3.iter() { self.use_bomb[nb.0][nb.1] = false; for &np in input.can_hakai[nb.0][nb.1].iter() { self.cnt_use[np] -= 1; } } self.jun.retain(|x| !rireki.3.contains(x)); self.use_bomb[rireki.2 .0][rireki.2 .1] = true; self.jun.insert(rireki.1, rireki.2); } fn insert_jun(&mut self) -> ((Coord, usize), usize, usize) { let moto = rnd::next() % self.jun.len(); let b = self.jun[moto]; self.jun.remove(moto); let tugi = rnd::next() % self.jun.len(); self.jun.insert(tugi, b); (b, moto, tugi) } fn undo_insert(&mut self, rireki: ((Coord, usize), usize, usize)) { self.jun.remove(rireki.2); self.jun.insert(rireki.1, rireki.0); } } fn annealing(input: &Input, duration: f64, mut state: State) -> State { let start_time = get_time(); let duration_inv = 1.0 / duration; let start_temp: f64 = state.score as f64 / 30.0; let end_temp: f64 = start_temp / 5000.0; let diff_temp: f64 = end_temp / start_temp; let mut temp = start_temp; let update_temp = || { // 非線形 return start_temp * diff_temp.powf((get_time() - start_time) * duration_inv); // 線形 //return start_temp + (end_temp - start_temp) * (get_time() - start_time) * duration_inv; }; let loglis = (0..0x10000) .map(|i| (i as f64 / 0xffff as f64).ln()) .collect::>(); fn is_accepted(now_score: usize, next_score: usize, temp: f64, loglis: &Vec) -> bool { // 最大化する場合。最小化時は -(next_score - now_score) -(next_score as f64 - now_score as f64) >= temp * loglis[rnd::next() & 0xffff] } let mut best_state = state.clone(); let mut iter_sa = 0; let mut cnt_kousin = 0; let mut cnt_kousin_best = 0; loop { if iter_sa & 255 == 0 { if get_time() - start_time > duration { break; } // kick! if mae_kousin > 100 {} temp = update_temp(); } iter_sa += 1; let op = rnd::next() % 100; if op < 10 { let now_score = state.score; let pos = select_2(state.jun.len()); state.swap_jun(pos); state.calc_score_4(input); let next_score = state.score; if is_accepted(now_score, next_score, temp, &loglis) { cnt_kousin += 1; } else { state.swap_jun(pos); state.score = now_score; } } else if op < 40 { let now_score = state.score; let rireki = state.insert_jun(); state.calc_score_4(input); let next_score = state.score; if is_accepted(now_score, next_score, temp, &loglis) { cnt_kousin += 1; } else { state.undo_insert(rireki); state.score = now_score; } } else { let now_score = state.score; let rireki = state.swap_off2(input); if !rireki.0 { state.undo_swap_off(input, rireki); continue; } state.calc_score_4(input); let next_score = state.score; if is_accepted(now_score, next_score, temp, &loglis) { cnt_kousin += 1; } else { // state.undo_swap_off(input, rireki); state.score = now_score; } } if best_state.score > state.score { best_state = state.clone(); cnt_kousin_best += 1; } } crate::print_data!(iter_sa); crate::print_data!(cnt_kousin); crate::print_data!(cnt_kousin_best); best_state } fn moves(a: Coord, b: Coord) -> Vec<(usize, usize)> { let mut ret = vec![]; let mut p = a; while p != b { if p.y > b.y { p.y -= 1; ret.push((1, 0)); } else if p.x > b.x { p.x -= 1; ret.push((1, 1)); } else if p.y < b.y { p.y += 1; ret.push((1, 2)); } else if p.x < b.x { p.x += 1; ret.push((1, 3)); }; } ret } fn moves_dijkstra(a: Coord, b: Coord, bombed: &Map2d, input: &Input) -> Vec<(usize, usize)> { let mut ret = vec![]; if a == b { return ret; } let mut dp = Map2d::new(vec![(!0usize, !0); N2], N); dp[a] = (0, !0); let mut que = BinaryHeap::new(); que.push((Reverse(0), a)); while let Some((Reverse(dist), p)) = que.pop() { if que.len() > 10000 { assert!(1 == 0); } if p == b { break; } for dir in 0..4 { let np = p.next(dir); if !np.in_map(N) { continue; } let ncost = if input.map[np] == Square::Empty || bombed[np] { dist + 1 } else { dist + 2 }; if ncost < dp[np].0 { dp[np] = (ncost, dir); que.push((Reverse(ncost), np)); } } } assert!(dp[b].1 != !0); let mut p = b; while p != a { ret.push((1, dp[p].1)); p = p.next((2 + dp[p].1) % 4); } ret.reverse(); ret } fn calc_dist_dijkstra(a: Coord, b: Coord, bombed: &Map2d, input: &Input) -> usize { let mut dp = Map2d::new(vec![!0usize; N2], N); dp[a] = 0; let mut que = BinaryHeap::new(); que.push((Reverse(0), a)); while let Some((Reverse(dist), p)) = que.pop() { if p == b { return dist; } if dist > dp[p] { continue; } for dir in 0..4 { let np = p.next(dir); if !np.in_map(N) { continue; } let ncost = if input.map[np] == Square::Empty || bombed[np] { dist + 1 } else { dist + 2 }; if ncost < dp[np] { dp[np] = ncost; que.push((Reverse(ncost), np)); } } } unreachable!() } #[derive(PartialEq, Eq, Clone, Copy)] enum Square { Empty, House, Shop, } struct Input { map: Map2d, bomb_cost: Vec, shops: Vec, can_hakai: Map2d>>, // そのマスで爆弾iを使って破壊できるもの can_hakai_rev: Map2d>, // そのマスを破壊することができるマスと爆弾の種類 can_hakai_shop: Map2d>>, near_shop: Map2d>, } fn read_input() -> Input { let _nm = input_vecusize(); let mut map_ = vec![]; for _i in 0..N { let a = input_string(); map_.push(a.chars().collect::>()); } let mut map = vec![]; for x in 0..N { for y in 0..N { let c = match map_[x][y] { '.' => Square::Empty, '#' => Square::House, '@' => Square::Shop, _ => unreachable!(), }; map.push(c); } } let map = Map2d::new(map, N); let mut can_hakai = Map2d::new(vec![vec![vec![]; M]; N2], N); let mut can_hakai_shop = Map2d::new(vec![vec![vec![]; M]; N2], N); let mut bomb_cost = vec![]; let mut can_hakai_rev = Map2d::new(vec![vec![]; N2], N); for bombid in 0..M { let cl = input_vecusize(); let c = cl[0]; let l = cl[1]; bomb_cost.push(c); for _ in 0..l { let dxdy = input_veci32(); let dx = dxdy[0]; let dy = dxdy[1]; for x in 0..N as i32 { for y in 0..N as i32 { let nx = x + dx; let ny = y + dy; let p = Coord::new(x as usize, y as usize); let np = Coord::new(nx as usize, ny as usize); if 0 <= nx && nx < N as i32 && 0 <= ny && ny < N as i32 && map[np] != Square::Empty { can_hakai[p][bombid].push(np); can_hakai_rev[np].push((p, bombid)); if map[np] == Square::Shop { can_hakai_shop[p][bombid].push(np); } } } } } } let mut shops = vec![]; for x in 0..N { for y in 0..N { let p = Coord::new(x, y); if map[p] == Square::Shop { shops.push(p); } } } let mut near_shop = Map2d::new(vec![vec![]; N2], N); for x in 0..N { for y in 0..N { let p = Coord::new(x, y); near_shop[p] = shops.clone(); near_shop[p].sort_by(|a, b| p.dist(a).cmp(&p.dist(b))); } } Input { map, bomb_cost, shops, can_hakai, can_hakai_rev, can_hakai_shop, near_shop, } } struct Output { score: usize, actions: Vec<(usize, usize)>, } impl Output { fn print_ans(&self) { //let cost = self.score; //crate::print_data!(cost); //let score = 1000000000000usize / (10000 + cost); //crate::print_data!(score); let q = self.actions.len(); println!("{}", q); for i in 0..q { if self.actions[i].0 == 1 { println!("{} {}", self.actions[i].0, DC[self.actions[i].1]); } else { println!("{} {}", self.actions[i].0, self.actions[i].1); } } } } // ここからライブラリ #[macro_export] macro_rules! print_data { ( $($x:expr),* ) => {{ $(eprintln!("[DATA] {} = {:?}", stringify!($x), $x);)* }} } pub fn get_time() -> f64 { static mut START: f64 = -1.0; let end = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs_f64(); unsafe { if START < 0.0 { START = end; } end - START } } #[allow(unused)] mod rnd { static mut S: usize = 88172645463325252; pub fn next() -> usize { unsafe { S ^= S << 7; S ^= S >> 9; S } } pub fn nextf() -> f64 { f64::from_bits((0x3ff0000000000000 | next() & 0xfffffffffffff) as u64) - 1. } pub fn range(a: usize, b: usize) -> usize { next() % (b - a) + a } pub fn exu(a: usize, b: usize, skip: usize) -> usize { assert!(a <= skip && skip < b); let ret = range(a, b - 1); ret + (skip <= ret) as usize } pub fn rangei(a: i64, b: i64) -> i64 { (next() % ((b - a) as usize)) as i64 + a } pub fn shuffle(list: &mut [T]) { for i in (0..list.len()).rev() { list.swap(i, next() % (i + 1)); } } } #[allow(dead_code)] mod grid { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Coord { pub x: usize, pub y: usize, } impl Coord { pub const fn new(x: usize, y: usize) -> Self { Self { x, y } } pub const fn new2(p: usize, width: usize) -> Self { Self { x: p / width, y: p % width, } } pub fn in_map(&self, size: usize) -> bool { self.x < size && self.y < size } pub const fn idx(&self, size: usize) -> usize { self.x * size + self.y } pub fn next(self, dir: usize) -> Self { self + DXY[dir] } pub const fn dist(&self, other: &Self) -> usize { Self::dist_1d(self.x, other.x) + Self::dist_1d(self.y, other.y) } const fn dist_1d(x0: usize, x1: usize) -> usize { x0.abs_diff(x1) } } impl std::ops::Add for Coord { type Output = Self; fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y, } } } pub const DXY: [Coord; 4] = [ Coord::new(0, !0), Coord::new(!0, 0), Coord::new(0, 1), Coord::new(1, 0), ]; pub const DC: [char; 4] = ['L', 'U', 'R', 'D']; #[derive(Debug, Clone)] pub struct Map2d { pub height: usize, pub width: usize, map: Vec, } impl Map2d { pub fn new(map: Vec, width: usize) -> Self { let height = map.len() / width; debug_assert!(width * height == map.len()); Self { height, width, map } } pub fn fill(&mut self, value: T) { self.map.fill(value); } } impl std::ops::Index for Map2d { type Output = T; #[inline] fn index(&self, coord: Coord) -> &Self::Output { unsafe { self.map.get_unchecked(coord.x * self.width + coord.y) } } } impl std::ops::IndexMut for Map2d { #[inline] fn index_mut(&mut self, coord: Coord) -> &mut Self::Output { unsafe { self.map.get_unchecked_mut(coord.x * self.width + coord.y) } } } impl std::ops::Index<&Coord> for Map2d { type Output = T; #[inline] fn index(&self, coord: &Coord) -> &Self::Output { &self.map[coord.x * self.width + coord.y] } } impl std::ops::IndexMut<&Coord> for Map2d { #[inline] fn index_mut(&mut self, coord: &Coord) -> &mut Self::Output { &mut self.map[coord.x * self.width + coord.y] } } impl std::ops::Index for Map2d { type Output = T; #[inline] fn index(&self, p: usize) -> &Self::Output { &self.map[p] } } impl std::ops::IndexMut for Map2d { #[inline] fn index_mut(&mut self, p: usize) -> &mut Self::Output { &mut self.map[p] } } } fn input_vecusize() -> Vec { let mut a = String::new(); stdin().read_line(&mut a).unwrap(); return a .trim() .split_whitespace() .map(|e| e.parse().ok().unwrap()) .collect(); } fn input_veci32() -> Vec { let mut a = String::new(); stdin().read_line(&mut a).unwrap(); return a .trim() .split_whitespace() .map(|e| e.parse().ok().unwrap()) .collect(); } fn input_string() -> String { let mut a = String::new(); stdin().read_line(&mut a).unwrap(); return a.trim().parse().unwrap(); } fn select_2(r: usize) -> (usize, usize) { let a = rnd::next() % r; let b = rnd::exu(0, r, a); (a, b) }