結果
問題 | No.5007 Steiner Space Travel |
ユーザー | r3yohei |
提出日時 | 2023-04-27 11:08:59 |
言語 | Rust (1.77.0 + proconio) |
結果 |
AC
|
実行時間 | 2 ms / 1,000 ms |
コード長 | 15,291 bytes |
コンパイル時間 | 2,128 ms |
コンパイル使用メモリ | 163,124 KB |
実行使用メモリ | 4,376 KB |
スコア | 6,669,031 |
最終ジャッジ日時 | 2023-04-27 11:09:07 |
合計ジャッジ時間 | 3,941 ms |
ジャッジサーバーID (参考情報) |
judge11 / judge15 |
純コード判定しない問題か言語 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 2 ms
4,372 KB |
testcase_01 | AC | 1 ms
4,368 KB |
testcase_02 | AC | 2 ms
4,368 KB |
testcase_03 | AC | 2 ms
4,368 KB |
testcase_04 | AC | 2 ms
4,372 KB |
testcase_05 | AC | 2 ms
4,368 KB |
testcase_06 | AC | 1 ms
4,368 KB |
testcase_07 | AC | 2 ms
4,368 KB |
testcase_08 | AC | 2 ms
4,372 KB |
testcase_09 | AC | 2 ms
4,368 KB |
testcase_10 | AC | 1 ms
4,368 KB |
testcase_11 | AC | 1 ms
4,372 KB |
testcase_12 | AC | 2 ms
4,372 KB |
testcase_13 | AC | 2 ms
4,372 KB |
testcase_14 | AC | 2 ms
4,372 KB |
testcase_15 | AC | 1 ms
4,376 KB |
testcase_16 | AC | 1 ms
4,372 KB |
testcase_17 | AC | 2 ms
4,372 KB |
testcase_18 | AC | 1 ms
4,368 KB |
testcase_19 | AC | 2 ms
4,372 KB |
testcase_20 | AC | 1 ms
4,368 KB |
testcase_21 | AC | 2 ms
4,368 KB |
testcase_22 | AC | 2 ms
4,372 KB |
testcase_23 | AC | 2 ms
4,372 KB |
testcase_24 | AC | 2 ms
4,368 KB |
testcase_25 | AC | 1 ms
4,368 KB |
testcase_26 | AC | 2 ms
4,368 KB |
testcase_27 | AC | 2 ms
4,368 KB |
testcase_28 | AC | 2 ms
4,372 KB |
testcase_29 | AC | 1 ms
4,368 KB |
ソースコード
#![allow(non_snake_case, unused)] use std::io::*; use std::{collections::*, fmt::format}; use std::{cmp::*, vec}; use crate::input::{*, marker::*}; use self::i64::kmeans; // コピペで使える proconio もどき // cunitacさんからお借りしています // https://gist.github.com/cunitac/b00be62bf68c9fb6055d22eb77c17e14 pub mod input { use std::{ cell::RefCell, fmt::Debug, io::{stdin, BufRead, BufReader, Stdin}, str::{FromStr, SplitWhitespace}, }; thread_local!( pub static STDIN_SOURCE: RefCell<Source> = RefCell::new(Source { stdin: BufReader::new(stdin()), tokens: "".split_whitespace(), }); ); pub struct Source { stdin: BufReader<Stdin>, tokens: SplitWhitespace<'static>, } impl Source { pub fn next_token(&mut self) -> Option<&str> { self.tokens.next().or_else(|| { let mut input = String::new(); self.stdin.read_line(&mut input).unwrap(); self.tokens = Box::leak(input.into_boxed_str()).split_whitespace(); self.tokens.next() }) } } #[macro_export] macro_rules! read_value { (from $s:expr, [$t:tt; $n:expr]) => { (0..$n).map(|_| $crate::read_value!(from $s, $t)).collect::<Vec<_>>() }; (from $s:expr, [$t:tt]) => { let n = $crate::read_value!(from $s, usize); $crate::read_value!(from $s, [$t; n]) }; (from $s:expr, $t:ty) => { <$t as $crate::input::Readable>::read(&mut $s) }; (from $s:expr, $($t:tt),* $(,)?) => { ($($crate::read_value!(from $s, $t)),*) }; ($($r:tt)*) => { $crate::input::STDIN_SOURCE.with(|s| { let mut s = s.borrow_mut(); $crate::read_value!(from s, $($r)*) }) } } #[macro_export] macro_rules! input { () => { }; ($x:tt: $t:tt, $($r:tt)*) => { let $x = $crate::read_value!($t); $crate::input!($($r)*); }; (mut $x:tt: $t:tt, $($r:tt)*) => { let mut $x = $crate::read_value!($t); $crate::input!($($r)*); }; (from $s:expr, $x:tt, $t:tt, $($r:tt)*) => { let $x = $crate::read_value!(from $s, $t); $crate::input!(from $s, $($r)*); }; (from $s:expr, mut $x:tt, $t:tt, $($r:tt)*) => { let mut $x = $crate::read_value!(from $s, $t); $crate::input!(from $s, $($r)*); }; ($($r:tt)*) => { $crate::input!($($r)*,); }; } pub trait Readable { type Output; fn read(source: &mut Source) -> Self::Output; } impl<T: FromStr<Err = E>, E: Debug> Readable for T { type Output = T; fn read(source: &mut Source) -> T { source.next_token().unwrap().parse().unwrap() } } pub mod marker { macro_rules! impl_readable { ($e:ident, $t:ty, $u:ty, $f:expr) => { pub enum $e {} impl $crate::input::Readable for $e { type Output = $t; fn read(mut source: &mut $crate::input::Source) -> $t { $f($crate::read_value!(from source, $u)) } } }; } impl_readable!(Usize1, usize, usize, |x| x - 1); impl_readable!(Isize1, isize, isize, |x| x - 1); impl_readable!(Chars, Vec<char>, String, |x: String| x.chars().collect()); impl_readable!(Bytes, Vec<u8>, String, |x: String| x.bytes().collect()); } } macro_rules! impl_kmeans { ($kind: ty, $modname: ident) => { // Since we can't overload methods in rust, we have to use namespacing pub mod $modname { use std::$modname::MAX; /// computes sum of squared deviation between two identically sized vectors /// `x`, and `y`. fn distance(x: &[$kind], y: &[$kind]) -> $kind { x.iter() .zip(y.iter()) .fold(0, |dist, (&xi, &yi)| dist + (xi - yi) * (xi - yi)) } /// Returns a vector containing the indices z<sub>i</sub> in {0, ..., K-1} of /// the centroid nearest to each datum. fn nearest_centroids(xs: &[Vec<$kind>], centroids: &[Vec<$kind>]) -> Vec<usize> { xs.iter() .map(|xi| { // Find the argmin by folding using a tuple containing the argmin // and the minimum distance. let (argmin, _) = centroids.iter().enumerate().fold( (0_usize, MAX), |(min_ix, min_dist), (ix, ci)| { let dist = distance(xi, ci); if dist < min_dist { (ix, dist) } else { (min_ix, min_dist) } }, ); argmin }) .collect() } /// Recompute the centroids given the current clustering fn recompute_centroids( xs: &[Vec<$kind>], clustering: &[usize], k: usize, ) -> Vec<Vec<$kind>> { let ndims = xs[0].len(); // NOTE: Kind of inefficient because we sweep all the data from each of the // k centroids. (0..k) .map(|cluster_ix| { let mut centroid: Vec<$kind> = vec![0; ndims]; let mut n_cluster: $kind = 0; xs.iter().zip(clustering.iter()).for_each(|(xi, &zi)| { if zi == cluster_ix { n_cluster += 1; xi.iter().enumerate().for_each(|(j, &x_ij)| { centroid[j] += x_ij; }); } }); centroid.iter().map(|&c_j| c_j / n_cluster).collect() }) .collect() } /// Assign the N D-dimensional data, `xs`, to `k` clusters using K-Means clustering pub fn kmeans(xs: Vec<Vec<$kind>>, k: usize) -> (Vec<usize>, Vec<Vec<$kind>>) { assert!(xs.len() >= k); // Rather than pulling in a dependency to radomly select the staring // points for the centroids, we're going to deterministally choose them by // slecting evenly spaced points in `xs` let n_per_cluster: usize = xs.len() / k; let centroids: Vec<Vec<$kind>> = (0..k).map(|j| xs[j * n_per_cluster].clone()).collect(); let mut clustering = nearest_centroids(&xs, ¢roids); loop { let centroids = recompute_centroids(&xs, &clustering, k); let new_clustering = nearest_centroids(&xs, ¢roids); // loop until the clustering doesn't change after the new centroids are computed if new_clustering .iter() .zip(clustering.iter()) .all(|(&za, &zb)| za == zb) { // We need to use `return` to break out of the `loop` return (clustering, centroids); } else { clustering = new_clustering; } } } } }; } impl_kmeans!(i64, i64); const N: usize = 100; const M: usize = 8; const ALPHA: i64 = 5; const INF: i64 = 1_000_000_000; const TL: f64 = 0.97; pub fn get_time() -> f64 { static mut STIME: f64 = -1.0; let t = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap(); let ms = t.as_secs() as f64 + t.subsec_nanos() as f64 * 1e-9; unsafe { if STIME < 0.0 { STIME = ms; } // ローカル環境とジャッジ環境の実行速度差はget_timeで吸収しておくと便利 #[cfg(feature = "local")] { (ms - STIME) * 1.5 } #[cfg(not(feature = "local"))] { (ms - STIME) } } } // 距離の二乗を返す pub fn squared_distance(x1: i64, y1: i64, type_1: usize, x2: i64, y2: i64, type_2: usize) -> i64 { if type_1 == 1 && type_2 == 1 { ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) * ALPHA * ALPHA } else if type_1 == 1 || type_2 == 1 { ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) * ALPHA } else { ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) } } #[derive(Clone)] pub struct Input { pos: Vec<Vec<i64>>, } impl Input { fn new() -> Self { input! { _n: usize, _m: usize, } let mut pos = vec![]; for _ in 0..N { input! { ai: i64, bi: i64, } pos.push(vec![ai, bi]); } Self { pos } } } // 初期解を貪欲法で作る pub fn init_route_greedy(input: &Input) -> Vec<(usize, usize)> { let mut route = vec![(1, 0)]; let mut visited = vec![false; N]; visited[0] = true; while route.len() < N { let &(crt_t, crt_r) = route.last().unwrap(); let mut min_dist = INF; let mut min_index = !0; for i in 1..N { if crt_r == i || visited[i] {continue;} let crt_pos = &input.pos[crt_r]; let next_pos = &input.pos[i]; let dist = squared_distance(crt_pos[0], crt_pos[1], crt_t, next_pos[0], next_pos[1], 1); if dist < min_dist { min_dist = dist; min_index = i; } } route.push((1, min_index)); visited[min_index] = true; } route.push((1, 0)); route } // 初期解をkmeansのクラスタ分けを参考にして作る pub fn init_route_kmeans(input: &Input, cluster: &Vec<usize>, station: &Vec<Vec<i64>>) -> Vec<(usize, usize)> { // 惑星1から惑星1が属するステーションに行く let mut route = vec![(1, 0)]; route.push((2, cluster[0])); let mut visited = vec![false; N]; visited[0] = true; let mut visited_cluster = vec![false; M]; visited_cluster[cluster[0]] = true; loop { // 惑星1は例えばcluster[0]に属する // cluster[0]に属する惑星のうち,station[cluster[0]]との距離が一定以下のものはスターグラフとしてつなぐ let &(crt_t, crt_r) = route.last().unwrap(); let crt_cluster = if crt_t == 1 { cluster[crt_r] } else { crt_r }; let crt_station = &station[crt_cluster]; let mut min_dist = INF; let mut min_index = !0; let mut is_updated = false; for i in 1..N { // 現在見ているクラスタに属する惑星のうちで,距離が一定以下のものをつなぐ if visited[i] || cluster[i] != crt_cluster {continue;} let crt_pos = if crt_t == 1 { &input.pos[crt_r] } else { &station[crt_cluster] }; let next_pos = &input.pos[i]; let dist = squared_distance(crt_pos[0], crt_pos[1], crt_t, next_pos[0], next_pos[1], 1); if dist < min_dist { min_dist = dist; min_index = i; is_updated = true; } if !is_updated {break;} } if is_updated { route.push((1, min_index)); route.push((2, crt_cluster)); visited[min_index] = true; visited_cluster[crt_cluster] = true; } // 現在のクラスタでの更新が終わったとき,そのクラスタから一番近い未訪問のステーションに移動する if !is_updated { let mut min_dist = INF; let mut min_index = !0; let mut is_updated_cluster = false; for i in 0..M { if visited_cluster[i] {continue;} let crt_pos = &station[crt_cluster]; let next_pos = &station[i]; let dist = squared_distance(crt_pos[0], crt_pos[1], 2, next_pos[0], next_pos[1], 2); if dist < min_dist { min_dist = dist; min_index = i; is_updated_cluster = true; } } if is_updated_cluster { route.push((2, min_index)); visited_cluster[min_index] = true; } if !is_updated_cluster {break;} } } route.push((1, 0)); route } #[derive(Clone)] struct State { turn: i64, is_done: bool, game_score: i64, evaluated_score: i64, station: Vec<Vec<i64>>, // i番目のステーションの場所 route: Vec<(usize, usize)>, // 道順 (惑星orステーション,番号) } impl State { fn new(input: &Input) -> Self { // 各惑星をkmeansでクラスタリングする // どのクラスタに所属するかと,各クラスタのセントロイドを保持し,セントロイドは宇宙ステーションとする let (cluster, station) = kmeans(input.pos.clone(), M); // let route = init_route_greedy(&input); let route = init_route_kmeans(&input, &cluster, &station); Self { turn: 0, is_done: false, game_score: 0, evaluated_score: 0, station, route, } } // 2-optを行う pub fn two_opt(&mut self) { let v = self.route.len(); } // 回答を出力 pub fn print(&self) { for i in 0..M { let station_i = &self.station[i]; println!("{} {}", station_i[0], station_i[1]); } let v = self.route.len(); println!("{}", v); for i in 0..v { let (ti, ri) = self.route[i]; println!("{} {}", ti, ri + 1); } } } fn main() { // 2-optに見えるが,再訪ありなので単純には適応できない // ただ,宇宙ステーションに再訪してもいいが,惑星は再訪しないほうがよい // 近傍: // - 2-opt (ただし,最初と最後が惑星1なのは固定する) // - 少なくとも一方が宇宙ステーションであるパスの追加 // - いずれかの宇宙ステーションの座標をずらす (あるいは,これはあらかじめk-meansで求めておく) get_time(); let input = Input::new(); let mut state = State::new(&input); state.print(); eprintln!("score: {}", state.game_score); eprintln!("time: {}", get_time()); }