結果

問題 No.5007 Steiner Space Travel
ユーザー r3yoheir3yohei
提出日時 2023-04-28 03:15:31
言語 Rust
(1.77.0 + proconio)
結果
AC  
実行時間 7 ms / 1,000 ms
コード長 15,645 bytes
コンパイル時間 10,708 ms
コンパイル使用メモリ 164,940 KB
実行使用メモリ 4,372 KB
スコア 8,131,067
最終ジャッジ日時 2023-04-28 03:15:45
合計ジャッジ時間 4,908 ms
ジャッジサーバーID
(参考情報)
judge14 / judge11
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 6 ms
4,368 KB
testcase_01 AC 6 ms
4,372 KB
testcase_02 AC 6 ms
4,368 KB
testcase_03 AC 6 ms
4,368 KB
testcase_04 AC 6 ms
4,368 KB
testcase_05 AC 6 ms
4,368 KB
testcase_06 AC 5 ms
4,368 KB
testcase_07 AC 6 ms
4,368 KB
testcase_08 AC 6 ms
4,372 KB
testcase_09 AC 6 ms
4,368 KB
testcase_10 AC 5 ms
4,368 KB
testcase_11 AC 6 ms
4,368 KB
testcase_12 AC 6 ms
4,372 KB
testcase_13 AC 6 ms
4,368 KB
testcase_14 AC 5 ms
4,368 KB
testcase_15 AC 6 ms
4,372 KB
testcase_16 AC 5 ms
4,372 KB
testcase_17 AC 5 ms
4,368 KB
testcase_18 AC 5 ms
4,368 KB
testcase_19 AC 5 ms
4,372 KB
testcase_20 AC 6 ms
4,372 KB
testcase_21 AC 6 ms
4,368 KB
testcase_22 AC 5 ms
4,372 KB
testcase_23 AC 6 ms
4,372 KB
testcase_24 AC 6 ms
4,372 KB
testcase_25 AC 5 ms
4,368 KB
testcase_26 AC 6 ms
4,372 KB
testcase_27 AC 7 ms
4,368 KB
testcase_28 AC 6 ms
4,368 KB
testcase_29 AC 5 ms
4,372 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#![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, &centroids);

                loop {
                    let centroids = recompute_centroids(&xs, &clustering, k);
                    let new_clustering = nearest_centroids(&xs, &centroids);

                    // 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(xi: i64, y1: i64, i: usize, xj: i64, y2: i64, j: usize) -> i64 {
    if i < N && j < N {
        ((xi - xj) * (xi - xj) + (y1 - y2) * (y1 - y2)) * ALPHA * ALPHA
    } else if i < N || j < N {
        ((xi - xj) * (xi - xj) + (y1 - y2) * (y1 - y2)) * ALPHA
    } else {
        ((xi - xj) * (xi - xj) + (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 warshall_floyd(pos: &Vec<Vec<i64>>) -> Vec<Vec<i64>> {
    let mut dist = vec![vec![INF; N+M]; N+M];
    // 自身への距離は0
    for i in 0..N+M {
        dist[i][i] = 0;    
    }
    // (i,j)間の距離を入れる
    for i in 0..N+M {
        for j in i+1..N+M {
            dist[i][j] = squared_distance(pos[i][0], pos[i][1], i, pos[j][0], pos[j][1], j);
            dist[j][i] = dist[i][j];
        }
    }
    // ワーシャルフロイド法
    for k in 0..N+M {
        for i in 0..N+M {
            for j in 0..N+M {
                // (i,j)間はkを経由したほうが短くなるか調べる
                if dist[i][j] > dist[i][k] + dist[k][j] {
                    dist[i][j] = dist[i][k] + dist[k][j];
                }
            }
        }
    }
    dist
}

// (i, j)間の最短パスをダイクストラで見つける
pub fn dijkstra(pos: &Vec<Vec<i64>>, s: usize, t: usize) -> Vec<(usize, usize)> {
    // s->tパスをダイクストラで見つける
    let mut dist = vec![INF; N+M];
    dist[s] = 0;
    let mut prev = vec![!0; N+M]; // 経路復元用
    let mut bh = BinaryHeap::new();
    bh.push((Reverse(0), s));
    while let Some((Reverse(d), frm)) = bh.pop() {
        // 目的地についているなら,終了する
        if frm == t {break;}
        // 見ようとしているものより既にいい経路が見つかっていれば飛ばす
        if dist[frm] < d {continue;}
        for to in 0..N+M {
            let energy = squared_distance(pos[frm][0], pos[frm][1], frm, pos[to][0], pos[to][1], to);
            if d + energy < dist[to] {
                // コストを更新したほうがいいなら,更新しつつ優先度付きキューに入れる
                dist[to] = d + energy;
                prev[to] = frm; // 頂点 frm -> toにたどり着いた
                bh.push((Reverse(dist[to]), to));
            }
        }
    }
    // 経路復元
    let mut path = vec![];
    let mut tt = t;
    while tt != !0 {
        // toがstartになるまでprev[to]を辿っていく
        if tt < N {
            // 惑星
            path.push((1, tt));
        } else {
            // ステーション
            path.push((2, tt - N));
        }
        tt = prev[tt];
    }
    path.reverse();
    path
}

// 始点sから開始して,未訪問の惑星で一番近いものへのパスを経路復元つきダイクストラで求めていく
pub fn init_path(pos: &Vec<Vec<i64>>, dist_warshall_floyd: &Vec<Vec<i64>>) -> Vec<(usize, usize)> {
    let mut path = vec![];
    let mut s = 0;
    let mut visited = vec![false; N];
    visited[s] = true;
    for i in 0..N {
        // sから未訪問の惑星のうち,一番近いものを見つける
        let mut min_dist = INF;
        let mut t = !0;
        if i < N - 1 {
            // 惑星1以外のN-1個の探索
            for j in 0..N {
                if s == j || visited[j] {continue;}
                if dist_warshall_floyd[s][j] < min_dist {
                    min_dist = dist_warshall_floyd[s][j];
                    t = j;
                }
            }
        } else {
            // 最後は惑星1に帰ってきたいので,visitedを無視してt=0にする
            t = 0;
        }
        // s->tパスをダイクストラで見つけ,全体に追加
        let mut small_path = dijkstra(&pos, s, t);
        if i < N - 1 {
            // 最後に惑星1に帰ってくるまでは,終点は次の始点になり,次のsmall_pathの先頭に含まれるので捨てる
            small_path.pop();
        }
        path.extend(small_path);
        // 次のイテレーションのための更新
        s = t;
        visited[t] = true;
    }
    path
}


#[derive(Clone)]
struct State {
    turn: i64,
    is_done: bool,
    game_score: i64,
    evaluated_score: i64,
    pos: Vec<Vec<i64>>, // 先頭N個に惑星の場所,続くM個にステーションの場所が格納されている
    path: Vec<(usize, usize)>, // 道順 (惑星orステーション,番号)
}
impl State {
    fn new(input: &Input) -> Self {
        // 各惑星をkmeansでクラスタリングする
        // どのクラスタに所属するかと,各クラスタのセントロイドを保持し,セントロイドは宇宙ステーションとする
        let (cluster, station) = kmeans(input.pos.clone(), M);
        let mut pos = input.pos.clone();
        pos.extend(station);
        // 惑星と宇宙ステーションの位置をもとに,(i, j)間の距離をワーシャルフロイドで求めておく
        let dist_warshall_floyd = warshall_floyd(&pos);
        let path = init_path(&pos, &dist_warshall_floyd);
        Self {
            turn: 0,
            is_done: false,
            game_score: 0,
            evaluated_score: 0,
            pos,
            path,
        }
    }
    // 2-optを行う
    // pub fn two_opt(&mut self) {
    //     // let v = self.route.len();
    // }
    // 1-indexに直しつつ回答を出力
    pub fn print(&self) {
        for i in 0..M {
            println!("{} {}", self.pos[i + N][0], self.pos[i + N][1]);
        }
        let v = self.path.len();
        println!("{}", v);
        for i in 0..v {
            let (ti, ri) = self.path[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());
}
0