#![allow(dead_code)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] use crate::{ data_structures::{ grid::{Coord, Map2d}, *, }, problem::*, sa2::*, utils::*, }; use std::{cmp::*, collections::*}; const TIME_LIMIT: f64 = 0.95; const N: usize = 45; const W: usize = 5 * 10usize.pow(17); 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 mut state = State2::new(input); state.score = state.calc_score(input); let mut solver = SASolver::new(state.clone()); solver.run(input, TIME_LIMIT - get_time()); let sa_score = solver.state.score; let len = solver.state.jun.len(); let c0a = ((input.cards[0].a.abs_diff(W) as f64).log10() * 1000.0).round() / 1000.0; let c0b = ((input.cards[0].b.abs_diff(W) as f64).log10() * 1000.0).round() / 1000.0; crate::printdata!(sa_score, len, c0a, c0b); solver.state.to_output(input) } mod data_structures { use super::*; pub 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] } } } } mod problem { use super::*; type ScoreType = f64; #[derive(Debug, Clone)] pub struct State { pub score: ScoreType, pub penalty: ScoreType, pub cards: Vec, pub rireki: Vec>, // [i]: i個目のカードがmergeされたもう一方のカードのindexとmergeされるのカード pub cnt: usize, } impl State { pub fn new(input: &Input) -> Self { Self { score: 0.0, penalty: 0.0, cards: input.cards.clone(), rireki: vec![vec![]; input.n], cnt: 0, } } pub fn calc_score(&self) -> ScoreType { let diff_a = self.cards[0].a.abs_diff(W); let diff_b = self.cards[0].b.abs_diff(W); let diff = max(diff_a, diff_b); if self.cnt < 50 && false { // 0か0とiをmergeしたときのスコアを返す let mut ret = diff; for i in 1..self.cards.len() { let next_a = (self.cards[0].a + self.cards[i].a) / 2; let next_b = (self.cards[0].b + self.cards[i].b) / 2; let diff_a = next_a.abs_diff(W); let diff_b = next_b.abs_diff(W); let diff = max(diff_a, diff_b); ret = ret.min(diff); } return (ret as f64 + 1.0).log10(); } (diff as f64 + 1.0).log10() } pub fn apply(&mut self, action: (usize, usize)) { self.rireki[action.0].push((action.1, self.cards[action.0])); self.rireki[action.1].push((action.0, self.cards[action.1])); let next_a = (self.cards[action.0].a + self.cards[action.1].a) / 2; let next_b = (self.cards[action.0].b + self.cards[action.1].b) / 2; self.cards[action.0].a = next_a; self.cards[action.0].b = next_b; self.cards[action.1].a = next_a; self.cards[action.1].b = next_b; self.score = self.calc_score(); self.cnt += 1; self.penalty = (self.cnt as f64 - 50.0).max(0.0); } pub fn revert(&mut self, action: (usize, usize)) { assert!(self.rireki[action.0].len() > 0); assert!(self.rireki[action.1].len() > 0); let aa = self.rireki[action.0].len(); self.cards[action.0] = self.rireki[action.0].last().unwrap().1; self.cards[action.1] = self.rireki[action.1].last().unwrap().1; self.rireki[action.0].pop(); self.rireki[action.1].pop(); assert!(self.rireki[action.0].len() == aa - 1); self.score = self.calc_score(); self.cnt -= 1; self.penalty = (self.cnt as f64 - 50.0).max(0.0); } pub fn to_output(&mut self, input: &Input) -> Output { let mut actions = vec![]; while self.rireki.iter().any(|x| !x.is_empty()) { let u = rnd::get(input.n); if self.rireki[u].is_empty() { continue; } let v = self.rireki[u].last().unwrap().0; if self.rireki[v].last().unwrap().0 != u { continue; } actions.push((u, v)); self.rireki[u].pop(); self.rireki[v].pop(); } Output { score: self.score, actions, } } } #[derive(Debug, Clone)] pub struct State2 { pub score: ScoreType, pub penalty: ScoreType, pub jun: Vec, } impl State2 { pub fn new(input: &Input) -> Self { let mut jun = (0..input.n).rev().collect::>(); for _ in 0..0 { jun.insert(20, 0); } Self { score: 0.0, penalty: 0.0, jun, } } pub fn calc_score(&self, input: &Input) -> ScoreType { let mut cards = input.cards.clone(); for i in 0..self.jun.len() - 1 { let next = merge_cards(&cards[self.jun[i]], &cards[self.jun[i + 1]]); cards[self.jun[i]] = next; cards[self.jun[i + 1]] = next; } let diff_a = cards[0].a.abs_diff(W); let diff_b = cards[0].b.abs_diff(W); let diff = max(diff_a, diff_b); (diff as f64 + 1.0).log10() } pub fn swap(&mut self, i: usize, j: usize) { self.jun.swap(i, j); } pub fn insert(&mut self, i: usize, card_idx: usize) { self.jun.insert(i, card_idx); } pub fn remove(&mut self, i: usize) { self.jun.remove(i); } pub fn to_output(&mut self, _input: &Input) -> Output { let mut actions = vec![]; for i in 0..self.jun.len() - 1 { if self.jun[i] == self.jun[i + 1] { continue; } actions.push((self.jun[i], self.jun[i + 1])); } Output { score: self.score, actions, } } } fn merge_cards(x: &Card, y: &Card) -> Card { Card::new((x.a + y.a) / 2, (x.b + y.b) / 2) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Card { pub a: usize, pub b: usize, } impl Card { fn new(a: usize, b: usize) -> Self { Self { a, b } } } pub struct Input { pub n: usize, pub cards: Vec, } pub fn read_input() -> Input { let n = input_1::(); let mut cards = vec![]; for _ in 0..n { let v = input_vec1::(); cards.push(Card::new(v[0], v[1])); } Input { n, cards } } pub struct Output { pub score: ScoreType, pub actions: Vec<(usize, usize)>, } impl Output { pub fn print_ans(&self) { let score = 2e6 - 1e5 * self.score; crate::printdata!(score); assert!(self.actions.len() <= 50); println!("{}", self.actions.len()); for &(a, b) in self.actions.iter() { assert!(a != b); println!("{} {}", a + 1, b + 1); } } } fn input_1() -> T where ::Err: core::fmt::Debug, { let mut a = String::new(); std::io::stdin().read_line(&mut a).unwrap(); a.trim().parse().unwrap() } fn input_vec1() -> Vec { let mut a = String::new(); std::io::stdin().read_line(&mut a).unwrap(); a.trim() .split_whitespace() .map(|e| e.parse().ok().unwrap()) .collect() } } mod sa { use super::*; type ScoreType = f64; pub struct SASolver { start_time: f64, duration: f64, temp: f64, log_table: [f64; 65536], pub state: State, best_state: State, time: f64, mae_kousin_best: f64, regal_iter: usize, } impl SASolver { const GOAL: OptimizationGoal = OptimizationGoal::Min; const START_TEMP: f64 = 2.00; const END_TEMP: f64 = 0.1; const DIFF_TEMP: f64 = Self::END_TEMP / Self::START_TEMP; pub fn new(state: State) -> Self { let mut log_table = [0.0; 65536]; for i in 0..65536 { log_table[i] = ((i as f64 + 0.5) / 65536.0).ln(); } rnd::shuffle(&mut log_table); Self { start_time: 0.0, duration: 0.0, temp: 0.0, log_table, state: state.clone(), best_state: state.clone(), time: 0.0, mae_kousin_best: 0.0, regal_iter: 0, } } fn update(&mut self) -> bool { self.time = get_time(); if self.time - self.start_time > self.duration { return false; } // 非線形 //self.temp = // Self::START_TEMP * Self::DIFF_TEMP.powf((self.time - self.start_time) / self.duration); // 線形 //self.temp = Self::START_TEMP // + (Self::END_TEMP - Self::START_TEMP) * (get_time() - self.start_time) / self.duration; // self.temp = Self::START_TEMP * Self::DIFF_TEMP.powf(((self.time - self.start_time) / self.duration).powf(1.5)); true } fn select_neighborhood(&mut self, input: &Input, _iter: usize) -> Box { const CNT_NEIGHBORHOOD: usize = 2; static neighborhood_ratio: [usize; CNT_NEIGHBORHOOD] = [50, 100]; let op = rnd::get(100); if op < neighborhood_ratio[0] { Box::new(neighborhood::Apply::new(input, &self.state)) } else { Box::new(neighborhood::Revert::new(input, &self.state)) } } fn update_best_state(&mut self) -> bool { if self.state.cnt > 50 { return false; } if Self::GOAL == OptimizationGoal::Max { if self.state.score > self.best_state.score { self.best_state = self.state.clone(); self.mae_kousin_best = self.time; return true; } } else { if self.state.score < self.best_state.score { self.best_state = self.state.clone(); self.mae_kousin_best = self.time; return true; } } false } fn is_accepted( &mut self, _input: &Input, pre_score: (ScoreType, ScoreType), next_score: (ScoreType, ScoreType), iter_sa: usize, ) -> bool { self.regal_iter += 1; let ss = 0.1; let pre_score = pre_score.0 as f64 + ss * pre_score.1 as f64 * (self.time - self.start_time) / self.duration as f64; let next_score = next_score.0 as f64 + ss * next_score.1 as f64 * (self.time - self.start_time) / self.duration as f64; if Self::GOAL == OptimizationGoal::Max { next_score as f64 - pre_score as f64 >= self.temp as f64 * self.log_table[iter_sa & 65535] as f64 } else { -(next_score as f64 - pre_score as f64) >= self.temp as f64 * self.log_table[iter_sa & 65535] as f64 } } pub fn run(&mut self, input: &Input, duration: f64) { self.start_time = get_time(); self.duration = duration; let mut iter_sa = 0; let mut cnt_ac = 0; let mut cnt_upd_best = 0; while iter_sa & 127 != 0 || self.update() { iter_sa += 1; let pre_score = (self.state.score, self.state.penalty); let neighbor: Box = self.select_neighborhood(input, iter_sa); if neighbor.preprocess(input, &mut self.state) && self.is_accepted( input, pre_score, (self.state.score, self.state.penalty), iter_sa, ) { neighbor.postprocess(input, &mut self.state); if self.update_best_state() { cnt_upd_best += 1; } cnt_ac += 1; } else { neighbor.rollback(input, &mut self.state, pre_score); } } crate::printdata!(iter_sa); eprintln!("regal_iter: {}", self.regal_iter); eprintln!("cnt_ac: {}", cnt_ac); eprintln!("cnt_upd_best: {}", cnt_upd_best); eprintln!("mae_kousin_best: {}", self.mae_kousin_best); self.state = self.best_state.clone(); } } #[derive(PartialEq)] enum OptimizationGoal { Max, Min, } trait Neighbor { fn preprocess(&self, input: &Input, state: &mut State) -> bool; fn postprocess(&self, input: &Input, state: &mut State); fn rollback(&self, input: &Input, state: &mut State, pre_score: (ScoreType, ScoreType)); } mod neighborhood { use super::*; pub struct Apply { action: (usize, usize), } impl Apply { pub fn new(input: &Input, _state: &State) -> Self { let u = rnd::get(input.n); let v = rnd::range_skip(0, input.n, u); assert!(u != v); Self { action: (u, v) } } } impl Neighbor for Apply { fn preprocess(&self, _input: &Input, state: &mut State) -> bool { state.apply(self.action); true } fn postprocess(&self, _input: &Input, _state: &mut State) {} fn rollback( &self, _input: &Input, state: &mut State, _pre_score: (ScoreType, ScoreType), ) { state.revert(self.action); } } pub struct Revert { action: (usize, usize), } impl Revert { pub fn new(input: &Input, state: &State) -> Self { if state.penalty < 1e-9 { return Self { action: (!0, !0) }; } loop { let u = rnd::get(input.n); if state.rireki[u].len() == 0 { continue; } let v = state.rireki[u].last().unwrap().0; if u != state.rireki[v].last().unwrap().0 { continue; } return Self { action: (u, v) }; } } } impl Neighbor for Revert { fn preprocess(&self, _input: &Input, state: &mut State) -> bool { if self.action == (!0, !0) { return false; } state.revert(self.action); true } fn postprocess(&self, _input: &Input, _state: &mut State) {} fn rollback( &self, _input: &Input, state: &mut State, _pre_score: (ScoreType, ScoreType), ) { if self.action == (!0, !0) { return; } state.apply(self.action); } } } } mod sa2 { use super::*; type ScoreType = f64; pub struct SASolver { start_time: f64, duration: f64, temp: f64, log_table: [f64; 65536], pub state: State2, best_state: State2, time: f64, mae_kousin_best: f64, regal_iter: usize, } impl SASolver { const GOAL: OptimizationGoal = OptimizationGoal::Min; const START_TEMP: f64 = 2.00; const END_TEMP: f64 = 0.1; const DIFF_TEMP: f64 = Self::END_TEMP / Self::START_TEMP; pub fn new(state: State2) -> Self { let mut log_table = [0.0; 65536]; for i in 0..65536 { log_table[i] = ((i as f64 + 0.5) / 65536.0).ln(); } rnd::shuffle(&mut log_table); Self { start_time: 0.0, duration: 0.0, temp: 0.0, log_table, state: state.clone(), best_state: state.clone(), time: 0.0, mae_kousin_best: 0.0, regal_iter: 0, } } fn update(&mut self) -> bool { self.time = get_time(); if self.time - self.start_time > self.duration { return false; } // 非線形 //self.temp = // Self::START_TEMP * Self::DIFF_TEMP.powf((self.time - self.start_time) / self.duration); // 線形 //self.temp = Self::START_TEMP // + (Self::END_TEMP - Self::START_TEMP) * (get_time() - self.start_time) / self.duration; // self.temp = Self::START_TEMP * Self::DIFF_TEMP.powf(((self.time - self.start_time) / self.duration).powf(1.5)); true } fn select_neighborhood(&mut self, input: &Input, _iter: usize) -> Box { const CNT_NEIGHBORHOOD: usize = 3; static neighborhood_ratio: [usize; CNT_NEIGHBORHOOD] = [100, 97, 100]; let op = rnd::get(100); if op < neighborhood_ratio[0] { Box::new(neighborhood::Swap::new(input, &self.state)) } else if op < neighborhood_ratio[1] { Box::new(neighborhood::Insert::new(input, &self.state)) } else { Box::new(neighborhood::Remove::new(input, &self.state)) } } fn update_best_state(&mut self) -> bool { if Self::GOAL == OptimizationGoal::Max { if self.state.score > self.best_state.score { self.best_state = self.state.clone(); self.mae_kousin_best = self.time; return true; } } else { if self.state.score < self.best_state.score { self.best_state = self.state.clone(); self.mae_kousin_best = self.time; return true; } } false } fn is_accepted( &mut self, _input: &Input, pre_score: (ScoreType, ScoreType), next_score: (ScoreType, ScoreType), iter_sa: usize, ) -> bool { self.regal_iter += 1; let ss = 0.1; let pre_score = pre_score.0 as f64 + ss * pre_score.1 as f64 * (self.time - self.start_time) / self.duration as f64; let next_score = next_score.0 as f64 + ss * next_score.1 as f64 * (self.time - self.start_time) / self.duration as f64; if Self::GOAL == OptimizationGoal::Max { next_score as f64 - pre_score as f64 >= self.temp as f64 * self.log_table[iter_sa & 65535] as f64 } else { -(next_score as f64 - pre_score as f64) >= self.temp as f64 * self.log_table[iter_sa & 65535] as f64 } } pub fn run(&mut self, input: &Input, duration: f64) { self.start_time = get_time(); self.duration = duration; let mut iter_sa = 0; let mut cnt_ac = 0; let mut cnt_upd_best = 0; while iter_sa & 127 != 0 || self.update() { iter_sa += 1; if iter_sa & 127 == 0 && self.state.score > 10.0 && self.state.jun.len() == 45 && self.time - self.start_time > duration * 0.5 { self.state.insert(20, 0); self.state.score = self.state.calc_score(input); } let pre_score = (self.state.score, self.state.penalty); let neighbor: Box = self.select_neighborhood(input, iter_sa); if neighbor.preprocess(input, &mut self.state) && self.is_accepted( input, pre_score, (self.state.score, self.state.penalty), iter_sa, ) { neighbor.postprocess(input, &mut self.state); if self.update_best_state() { cnt_upd_best += 1; } cnt_ac += 1; } else { neighbor.rollback(input, &mut self.state, pre_score); } } crate::printdata!(iter_sa); eprintln!("regal_iter: {}", self.regal_iter); eprintln!("cnt_ac: {}", cnt_ac); eprintln!("cnt_upd_best: {}", cnt_upd_best); eprintln!("mae_kousin_best: {}", self.mae_kousin_best); self.state = self.best_state.clone(); } } #[derive(PartialEq)] enum OptimizationGoal { Max, Min, } trait Neighbor2 { fn preprocess(&self, input: &Input, state: &mut State2) -> bool; fn postprocess(&self, input: &Input, state: &mut State2); fn rollback(&self, input: &Input, state: &mut State2, pre_score: (ScoreType, ScoreType)); } mod neighborhood { use super::*; pub struct Swap { pub u: usize, pub v: usize, } impl Swap { pub fn new(_input: &Input, state: &State2) -> Self { if state.jun.len() < 3 { return Self { u: !0, v: !0 }; } let u = rnd::range(0, state.jun.len() - 1); let v = rnd::range_skip(0, state.jun.len() - 1, u); Self { u, v } } } impl Neighbor2 for Swap { fn preprocess(&self, input: &Input, state: &mut State2) -> bool { if self.u == !0 { return false; } state.swap(self.u, self.v); state.score = state.calc_score(input); true } fn postprocess(&self, _input: &Input, _state: &mut State2) { // } fn rollback( &self, _input: &Input, state: &mut State2, pre_score: (ScoreType, ScoreType), ) { if self.u == !0 { return; } state.swap(self.u, self.v); state.score = pre_score.0; state.penalty = pre_score.1; } } pub struct Insert { pub u: usize, pub card_idx: usize, } impl Insert { pub fn new(input: &Input, state: &State2) -> Self { if state.jun.len() > 50 { return Self { u: !0, card_idx: !0, }; } loop { let u = rnd::get(state.jun.len() - 1); let card_idx = rnd::get(input.n); if (u > 0 && state.jun[u - 1] == card_idx) || (u < state.jun.len() - 1 && state.jun[u] == card_idx) { continue; } return Self { u, card_idx }; } } } impl Neighbor2 for Insert { fn preprocess(&self, input: &Input, state: &mut State2) -> bool { if self.u == !0 { return false; } state.jun.insert(self.u, self.card_idx); state.score = state.calc_score(input); true } fn postprocess(&self, _input: &Input, _state: &mut State2) { // } fn rollback( &self, _input: &Input, state: &mut State2, pre_score: (ScoreType, ScoreType), ) { if self.u == !0 { return; } state.jun.remove(self.u); state.score = pre_score.0; state.penalty = pre_score.1; } } pub struct Remove { pub u: usize, pub card_idx: usize, } impl Remove { pub fn new(_input: &Input, state: &State2) -> Self { if state.jun.len() == 1 { return Self { u: !0, card_idx: !0, }; } loop { let u = rnd::get(state.jun.len()); let card_idx = state.jun[u]; if u > 0 && u < state.jun.len() - 1 && state.jun[u - 1] == state.jun[u + 1] { continue; } return Self { u, card_idx }; } } } impl Neighbor2 for Remove { fn preprocess(&self, input: &Input, state: &mut State2) -> bool { if self.u == !0 { return false; } state.jun.remove(self.u); state.score = state.calc_score(input); true } fn postprocess(&self, _input: &Input, _state: &mut State2) { // } fn rollback( &self, _input: &Input, state: &mut State2, pre_score: (ScoreType, ScoreType), ) { if self.u == !0 { return; } state.jun.insert(self.u, self.card_idx); state.score = pre_score.0; state.penalty = pre_score.1; } } } } mod utils { use super::*; #[macro_export] macro_rules! printdata { ( $($x:expr),* ) => {{ $(eprintln!("[DATA] {} = {:?}", stringify!($x), $x);)* }} } pub mod rnd { static mut A: u64 = 1; pub fn next() -> u32 { unsafe { let mut x = A; A *= 0xcafef00dd15ea5e5; x ^= x >> 22; (x >> 22 + (x >> 61)) as u32 } } pub fn next64() -> u64 { (next() as u64) << 32 | next() as u64 } pub fn nextf() -> f64 { unsafe { std::mem::transmute::(0x3ff0000000000000 | (next() as u64) << 20) - 1. } } pub fn get(n: usize) -> usize { assert!(n <= u32::MAX as usize); next() as usize * n >> 32 } pub fn range(a: usize, b: usize) -> usize { assert!(a < b); get(b - a) + a } pub fn range_skip(a: usize, b: usize, skip: usize) -> usize { assert!(a <= skip && skip < b); let n = range(a, b - 1); n + (skip <= n) as usize } pub fn rangei(a: i64, b: i64) -> i64 { assert!(a < b); get((b - a) as usize) as i64 + a } pub fn shuffle(list: &mut [T]) { for i in (0..list.len()).rev() { list.swap(i, get(i + 1)); } } pub fn shuffle_iter(list: &mut [T]) -> impl Iterator + '_ { (0..list.len()).rev().map(|i| { list.swap(i, get(i + 1)); list[i] }) } } pub trait RandomChoice { type Output; fn choice(&self) -> Self::Output; } impl RandomChoice for [T] { type Output = T; fn choice(&self) -> T { self[rnd::get(self.len())] } } 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 } } }