結果

問題 No.5007 Steiner Space Travel
ユーザー r3yoheir3yohei
提出日時 2023-06-25 10:50:09
言語 Rust
(1.77.0 + proconio)
結果
AC  
実行時間 999 ms / 1,000 ms
コード長 38,487 bytes
コンパイル時間 2,885 ms
コンパイル使用メモリ 182,764 KB
実行使用メモリ 4,372 KB
スコア 9,052,083
最終ジャッジ日時 2023-06-25 10:50:48
合計ジャッジ時間 35,693 ms
ジャッジサーバーID
(参考情報)
judge13 / judge14
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

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

ソースコード

diff #

#![allow(non_snake_case, unused)]
use self::i32::kmeans;
use crate::input::{marker::*, *};
use crate::rand::Xoshiro256;
use std::{cmp::*, vec};
use std::{collections::*, fmt::format};
use std::{hash, io::*};
use std::env;

// proconio
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());
    }
}

mod rand {
    pub(crate) struct Xoshiro256 {
        s0: u64,
        s1: u64,
        s2: u64,
        s3: u64,
    }

    impl Xoshiro256 {
        pub(crate) fn new(mut seed: u64) -> Self {
            let s0 = split_mix_64(&mut seed);
            let s1 = split_mix_64(&mut seed);
            let s2 = split_mix_64(&mut seed);
            let s3 = split_mix_64(&mut seed);
            Self { s0, s1, s2, s3 }
        }

        fn next(&mut self) -> u64 {
            let result = (self.s1.wrapping_mul(5)).rotate_left(7).wrapping_mul(9);
            let t = self.s1 << 17;

            self.s2 ^= self.s0;
            self.s3 ^= self.s1;
            self.s1 ^= self.s2;
            self.s0 ^= self.s3;
            self.s2 ^= t;
            self.s3 = self.s3.rotate_left(45);

            result
        }

        pub(crate) fn gen_usize(&mut self, lower: usize, upper: usize) -> usize {
            assert!(lower < upper);
            let count = upper - lower;
            (self.next() % count as u64) as usize + lower
        }

        pub(crate) fn gen_i32(&mut self, lower: i32, upper: i32) -> i32 {
            assert!(lower < upper);
            let count = upper - lower;
            (self.next() % count as u64) as i32 + lower
        }

        pub(crate) fn gen_f64(&mut self) -> f64 {
            const UPPER_MASK: u64 = 0x3ff0000000000000;
            const LOWER_MASK: u64 = 0xfffffffffffff;
            let result = UPPER_MASK | (self.next() & LOWER_MASK);
            let result: f64 = unsafe { std::mem::transmute(result) };
            result - 1.0
        }

        pub(crate) fn gen_bool(&mut self, prob: f64) -> bool {
            self.gen_f64() < prob
        }
    }

    fn split_mix_64(x: &mut u64) -> u64 {
        *x = x.wrapping_add(0x9e3779b97f4a7c15);
        let mut z = *x;
        z = (z ^ z >> 30).wrapping_mul(0xbf58476d1ce4e5b9);
        z = (z ^ z >> 27).wrapping_mul(0x94d049bb133111eb);
        z ^ z >> 31
    }
}

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!(i32, i32);

/*
[I 2023-06-25 10:41:46,398] Trial 2230 finished with value: 301121.87 and parameters: 
{'CALC_TEMP_FREQUENCY': 2851, 'KICK_THRESHOLD': 50621, 'MAX_DELTA': 257.236083625224, 'MIN_DELTA': 14.080818963193781, 
'MULTI_START': 1, 'NUM_STATION': 5, 'PROB_2OPT': 64, 'PROB_3OPT': 66, 'PROB_DOUBLE_BRIDGE': 0.756448734745971, 'PROB_INSERT': 77, 
'PROB_INSERT_STATION': 83, 'PROB_MOVE_STATION': 81, 'T1': 164.0285193747435, 'TO': 14096.678262676927}. 
Best is trial 2230 with value: 301121.87.
*/
const N: usize = 100;
const M: usize = 8;
const ALPHA: i32 = 5;
const MAP_SIZE: i32 = 1000;
const INF: i32 = 1_000_000_000;
const TL: f64 = 0.996;
const IS_ANNEALING: bool = true;
const T0: f64 = 14096.678262676927;
const T1: f64 = 164.0285193747435;
const MULTI_START: f64 = 1.0;
const PROB_2OPT: usize = 64;
const PROB_3OPT: usize = 66;
const PROB_INSERT: usize = 77;
const PROB_MOVE_STATION: usize = 81;
const PROB_INSERT_STATION: usize = 83;
const MAX_DELTA: f64 = 257.236083625224;
const MIN_DELTA: f64 = 14.080818963193781;
const KICK_THRESHOLD: usize = 50621;
const PROB_DOUBLE_BRIDGE: f64 = 0.756448734745971;
const CALC_TEMP_FREQUENCY: usize = 2851;
const NUM_STATION: usize = 5;

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
        }
    }
}

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(point_i: Position, i: usize, point_j: Position, j: usize) -> i32 {
    if i < N && j < N {
        ((point_i.x - point_j.x) * (point_i.x - point_j.x)
            + (point_i.y - point_j.y) * (point_i.y - point_j.y))
            * ALPHA
            * ALPHA
    } else if i < N || j < N {
        ((point_i.x - point_j.x) * (point_i.x - point_j.x)
            + (point_i.y - point_j.y) * (point_i.y - point_j.y))
            * ALPHA
    } else {
        ((point_i.x - point_j.x) * (point_i.x - point_j.x)
            + (point_i.y - point_j.y) * (point_i.y - point_j.y))
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Position {
    x: i32,
    y: i32,
}

impl Position {
    fn new(x: i32, y: i32) -> Self {
        Self { x, y }
    }
}

#[derive(Clone)]
pub struct Input {
    pos: Vec<Position>,  // 惑星の位置
    dist: Vec<Vec<i32>>, // ステーションを無視した惑星同士の距離
}
impl Input {
    fn new() -> Self {
        input! {
            _n: usize,
            _m: usize,
        }
        let mut pos = vec![];
        // kmeansでステーションの初期位置を定めるため,posを変換
        let mut pos_kmeans = vec![];
        for _ in 0..N {
            input! {
                ai: i32,
                bi: i32,
            }
            pos.push(Position::new(ai, bi));
            pos_kmeans.push(vec![ai, bi]);
        }
        // ステーションの効果を無視して惑星間でワーシャルフロイド
        let dist = warshall_floyd(&pos);
        // kmeansでステーションの初期位置を決める
        let (_, station_kmeans) = kmeans(pos_kmeans, NUM_STATION);
        let mut station = vec![];
        // kmeansした箇所
        for i in 0..NUM_STATION {
            station.push(Position::new(station_kmeans[i][0], station_kmeans[i][1]));
        }
        // kmeansしてない箇所はランダムに入れる
        let mut rng = Xoshiro256::new(16384);
        for i in NUM_STATION..M {
            station.push(Position::new(rng.gen_i32(0, 1000), rng.gen_i32(0, 1000)));
        }
        pos.extend(station);
        Self { pos, dist }
    }
}

// ワーシャルフロイドで全点対間最短経路を求める
pub fn warshall_floyd(pos: &Vec<Position>) -> Vec<Vec<i32>> {
    let mut dist = vec![vec![0; pos.len()]; pos.len()];
    // (i,j)間の距離を入れる
    for i in 0..pos.len() {
        for j in i + 1..pos.len() {
            dist[i][j] = squared_distance(pos[i], i, pos[j], j);
            dist[j][i] = dist[i][j];
        }
    }
    // ワーシャルフロイド法
    for k in 0..pos.len() {
        for i in 0..pos.len() {
            for j in 0..pos.len() {
                // (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<Position>, s: usize, t: usize) -> Vec<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], frm, pos[to], 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 != s {
        // toがstartになるまでprev[to]を辿っていく
        // 途中のエネルギーを保存しておく
        path.push(tt);
        tt = prev[tt];
    }
    path.reverse();
    path
}

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

#[derive(Clone)]
struct State {
    pos: Vec<Position>, // 先頭N個に惑星の場所,続くM個にステーションの場所が格納されている
    path: Vec<usize>,   // 道順 (惑星orステーション,番号)
}
impl State {
    fn new(input: &Input, greedy_path: &Vec<usize>) -> Self {
        Self {
            pos: input.pos.clone(),
            path: greedy_path.clone(),
        }
    }
    fn calc_score_all(&self, input: &Input) -> i32 {
        let mut score = 0;
        for w in self.path.windows(2) {
            let (prev, next) = (w[0], w[1]);
            score += self.calc_score(input, prev, next);
        }
        score
    }
    #[inline]
    fn calc_score(&self, input: &Input, prev: usize, next: usize) -> i32 {
        if prev < N && next < N {
            input.dist[prev][next]
        } else {
            squared_distance(self.pos[prev], prev, self.pos[next], next)
        }
    }
    // 1-indexに直しつつ回答を出力
    pub fn print(&self) {
        for i in 0..M {
            println!("{} {}", self.pos[i + N].x, self.pos[i + N].y);
        }
        let v = self.path.len();
        println!("{}", v);
        for &i in self.path.iter() {
            if i < N {
                println!("1 {}", i + 1);
            } else {
                println!("2 {}", i + 1 - N);
            }
        }
    }
}

fn restore_solution(input: &Input, solution: &State) -> State {
    let mut new_solution = solution.clone();
    new_solution.path.clear();
    new_solution.path.push(0);

    for w in solution.path.windows(2) {
        let (prev, next) = (w[0], w[1]);
        let path = dijkstra(&solution.pos, prev, next);
        for v in path {
            new_solution.path.push(v);
        }
    }

    new_solution
}

fn main() {
    get_time();
    let input = Input::new();
    let mut total_iter = 0;
    let mut each_iter = 0.; // 多点スタートの各イテレーション
    let mut accepted_count = 0;
    let mut update_best_count = 0;
    let mut double_bridge_count = 0;
    let mut rin_sakamichi_count = 0;
    // 初期解を貪欲に決定
    let mut greedy_path = vec![0]; // このdijkstraの経路復元はスタート位置を保持しないのでここで代入
    greedy_path.extend(init_path(&input));
    let mut best_state = State::new(&input, &greedy_path);
    let mut best_score = best_state.calc_score_all(&input);

    while get_time() < TL {
        // 多点スタートで振り出しに戻る箇所
        each_iter += 1.;
        // 乱数シードも変更する
        let mut rng = Xoshiro256::new(8192 + each_iter as u64);
        // stateを初期のものに変更する
        let mut state = State::new(&input, &greedy_path);
        let mut current_score = state.calc_score_all(&input);
        // 解を更新しなかった回数(閾値を超えたらdouble-bridgeでkickする)
        let mut not_accepted_count = 0;
        // 温度
        let mut t = get_time() / (TL * each_iter / MULTI_START);
        let mut T = T0.powf(1.0 - t) * T1.powf(t);

        // 特定の近傍の次は必ず2-optするようにする
        let mut is_select_2opt = false;
        // 各多点のイテレーションは,TL / MULTI_START秒ずつ割り当てられる
        while get_time() < TL * each_iter / MULTI_START {
            total_iter += 1;
            if total_iter % CALC_TEMP_FREQUENCY == 0 {
                t = get_time() / (TL * each_iter / MULTI_START);
                T = T0.powf(1.0 - t) * T1.powf(t);
            }
            // 乱数で生じさせた数値に応じて近傍を選択
            let neighbor_type = rng.gen_usize(0, 100);
            if is_select_2opt || neighbor_type < PROB_2OPT || get_time() > (TL * each_iter / MULTI_START) * 0.95 {
                // 2-opt
                // 最後の方は交差路をなくすために2-optばかりする
                // ある(u,next_u), (v,next_v)に対し,(u,v), (next_u, next_v) と繋ぎ変えたほうが嬉しいのか調べる
                let u = rng.gen_usize(0, state.path.len() - 2);
                let v = rng.gen_usize(u + 1, state.path.len() - 1);

                let i0 = state.path[u];
                let i1 = state.path[u + 1];
                let i2 = state.path[v];
                let i3 = state.path[v + 1];

                let d01 = state.calc_score(&input, i0, i1);
                let d23 = state.calc_score(&input, i2, i3);
                let d02 = state.calc_score(&input, i0, i2);
                let d13 = state.calc_score(&input, i1, i3);

                // スコア計算
                let score_diff = d02 + d13 - d01 - d23;
                let new_score = current_score + score_diff;

                if score_diff < 0 || IS_ANNEALING && rng.gen_bool(f64::exp(-score_diff as f64 / T))
                {
                    // 解の更新
                    current_score = new_score;
                    accepted_count += 1;
                    state.path[u + 1..v + 1].reverse();
                    is_select_2opt = false;
                } else {
                    not_accepted_count += 1;
                }
            } else if neighbor_type < PROB_3OPT {
                // 3-opt
                // 20230119 梅谷 メタヒューリスティクスの設計と実装 を参照
                // (u,next_u),(v,next_v),(w,next_w)のつなぎ替えのうちで最もいいものを採用する
                // type1(Or-opt): (u,next_v),(w,next_u),(v,next_w)
                // type2: (u,w),(next_v,next_u),(v,next_w)
                // type3: (u,next_v),(w,v),(next_u,next_w)
                // type4: (u,v),(next_u,w),(next_v,next_w)
                let u = rng.gen_usize(0, state.path.len() - 3);
                let v = rng.gen_usize(u + 1, state.path.len() - 2);
                let w = rng.gen_usize(v + 1, state.path.len() - 1);

                let i0 = state.path[u];
                let i1 = state.path[u + 1];
                let i2 = state.path[v];
                let i3 = state.path[v + 1];
                let i4 = state.path[w];
                let i5 = state.path[w + 1];

                let mut original_score = state.calc_score(&input, i0, i1);
                original_score += state.calc_score(&input, i2, i3);
                original_score += state.calc_score(&input, i4, i5);

                // type1
                // u -> v+1
                let mut type1_score = state.calc_score(&input, i0, i3);
                // w -> u+1
                type1_score += state.calc_score(&input, i4, i1);
                // v -> w+1
                type1_score += state.calc_score(&input, i2, i5);

                // type2
                // u -> w
                let mut type2_score = state.calc_score(&input, i0, i4);
                // v+1 -> u+1
                type2_score += state.calc_score(&input, i3, i1);
                // v -> w+1
                type2_score += state.calc_score(&input, i2, i5);

                // type3
                // u -> v+1
                let mut type3_score = state.calc_score(&input, i0, i3);
                // w -> v
                type3_score += state.calc_score(&input, i4, i2);
                // u+1 -> w+1
                type3_score += state.calc_score(&input, i1, i5);

                // type4
                // u -> v
                let mut type4_score = state.calc_score(&input, i0, i2);
                // u+1 -> w
                type4_score += state.calc_score(&input, i1, i4);
                // v+1 -> w+1
                type4_score += state.calc_score(&input, i3, i5);

                // スコア計算
                let each_type_score = vec![type1_score, type2_score, type3_score, type4_score];
                let mut score_diff = INF;
                let mut best_type = !0;
                for (idx, &type_score) in each_type_score.iter().enumerate() {
                    if score_diff.change_min(type_score - original_score) {
                        best_type = idx;
                    }
                }

                let new_score = current_score + score_diff;

                if score_diff < 0 || IS_ANNEALING && rng.gen_bool(f64::exp(-score_diff as f64 / T))
                {
                    // 解の更新
                    current_score = new_score;
                    accepted_count += 1;
                    // 採用された交換のtypeによって,pathの変え方を分岐する
                    let part1 = state.path[u + 1..v + 1].to_vec();
                    let part2 = state.path[v + 1..w + 1].to_vec();
                    if best_type == 0 {
                        // path[0..u,u+1..v,v+1..w,w+1..0]をpath[0..u,v+1..w,u+1..v,w+1..0]としたい
                        state.path.splice(
                            u + 1..w + 1,
                            part2.iter().cloned().chain(part1.iter().cloned()),
                        );
                    } else if best_type == 1 {
                        state.path.splice(
                            u + 1..w + 1,
                            part2.iter().rev().cloned().chain(part1.iter().cloned()),
                        );
                    } else if best_type == 2 {
                        state.path.splice(
                            u + 1..w + 1,
                            part2.iter().cloned().chain(part1.iter().rev().cloned()),
                        );
                    } else {
                        state.path.splice(
                            u + 1..w + 1,
                            part1
                                .iter()
                                .rev()
                                .cloned()
                                .chain(part2.iter().rev().cloned()),
                        );
                    }
                    is_select_2opt = true;
                } else {
                    not_accepted_count += 1;
                }
            } else if neighbor_type < PROB_INSERT {
                // 挿入近傍
                // あるu, vを選び,uをvの後ろへ入れる
                let u = rng.gen_usize(1, state.path.len() - 2);
                let v = rng.gen_usize(u + 1, state.path.len() - 1);

                let i0 = state.path[u - 1];
                let i1 = state.path[u];
                let i2 = state.path[u + 1];
                let i3 = state.path[v];
                let i4 = state.path[v + 1];

                let mut original_score = state.calc_score(&input, i0, i1);
                original_score += state.calc_score(&input, i1, i2);
                original_score += state.calc_score(&input, i3, i4);

                // u-1 -> u+1
                let mut insert_score = state.calc_score(&input, i0, i2);
                // v -> u
                insert_score += state.calc_score(&input, i3, i1);
                // u -> v+1
                insert_score += state.calc_score(&input, i1, i4);

                // スコア計算
                let score_diff = insert_score - original_score;
                let new_score = current_score + score_diff;

                if score_diff < 0 || IS_ANNEALING && rng.gen_bool(f64::exp(-score_diff as f64 / T))
                {
                    // 解の更新
                    current_score = new_score;
                    accepted_count += 1;
                    // path[0..u-1,u,u+1..v,v+1..0]をpath[0..u-1,u+1..v,u,v+1..0]としたい
                    let tmp = state.path[u];
                    state.path.remove(u);
                    state.path.insert(v, tmp);
                    is_select_2opt = true;
                } else {
                    not_accepted_count += 1;
                }
            } else if neighbor_type < PROB_MOVE_STATION {
                // あるstationを一旦削除し、ランダムにずらした上で、各辺でstationを使う/使わないを貪欲に決め直す
                let station_id = N + rng.gen_usize(0, M);
                let mut tmp_path = state.path.clone();
                tmp_path.retain(|&v| v != station_id);

                let old_p = state.pos[station_id];
                let mut p = old_p;
                let delta = (MAX_DELTA * (1. - t) + MIN_DELTA * t) as i32;
                p.x = rng.gen_i32((p.x - delta).max(0), (p.x + delta).min(MAP_SIZE) + 1);
                p.y = rng.gen_i32((p.y - delta).max(0), (p.y + delta).min(MAP_SIZE) + 1);
                state.pos[station_id] = p;
                let mut new_path = vec![];
                let mut new_score = 0;

                // path上の点で,新たにずらしたステーションを使った方がよいかどうかを判断する
                // warshall floydに近い気持ち
                for w in tmp_path.windows(2) {
                    let (prev, next) = (w[0], w[1]);
                    new_path.push(prev);
                    let old_dist = state.calc_score(&input, prev, next);
                    let new_dist = state.calc_score(&input, prev, station_id)
                        + state.calc_score(&input, station_id, next);
                    if new_dist < old_dist {
                        new_score += new_dist;
                        new_path.push(station_id);
                    } else {
                        new_score += old_dist;
                    }
                }
                // 惑星1に帰ってくる
                new_path.push(0);

                let score_diff = new_score - current_score;
                if score_diff < 0 || IS_ANNEALING && rng.gen_bool(f64::exp(-score_diff as f64 / T))
                {
                    // 解の更新
                    current_score = new_score;
                    accepted_count += 1;
                    state.path = new_path;
                } else {
                    // ロールバック
                    state.pos[station_id] = old_p;
                    not_accepted_count += 1;
                }
            } else if neighbor_type < PROB_INSERT_STATION {
                // stationを適当な位置に挿入する
                let station_id = N + rng.gen_usize(0, M);
                let index = rng.gen_usize(1, state.path.len());
                let prev = state.path[index - 1];
                let next = state.path[index];

                let old_score = state.calc_score(&input, prev, next);
                let new_score = state.calc_score(&input, prev, station_id)
                    + state.calc_score(&input, station_id, next);
                let score_diff = new_score - old_score;

                if score_diff <= 0 || rng.gen_bool(f64::exp(-score_diff as f64 / T)) {
                    // 解の更新
                    current_score += score_diff;
                    accepted_count += 1;
                    state.path.insert(index, station_id);
                } else {
                    not_accepted_count += 1;
                }
            } else {
                // 適当な位置のstationを削除する
                let mut index = 0;
                let mut trial = 0;
                while state.path[index] < N && trial < 10 {
                    index = rng.gen_usize(0, state.path.len());
                    trial += 1;
                }

                if state.path[index] < N {
                    continue;
                }

                let station_id = state.path[index];
                let prev = state.path[index - 1];
                let next: usize = state.path[index + 1];

                let old_score = state.calc_score(&input, prev, station_id)
                    + state.calc_score(&input, station_id, next);
                let new_score = state.calc_score(&input, prev, next);
                let score_diff = new_score - old_score;

                if score_diff <= 0 || rng.gen_bool(f64::exp(-score_diff as f64 / T)) {
                    // 解の更新
                    current_score += score_diff;
                    accepted_count += 1;
                    state.path.remove(index);
                } else {
                    not_accepted_count += 1;
                }
            }
            // どの近傍でもベスト更新は常に採用する
            if current_score < best_score {
                best_score = current_score;
                best_state = state.clone();
                update_best_count += 1;
                // eprintln!("update best: {}", best_score);
                // eprintln!("update iter: {}", iter);
            }
            // 近傍遷移回数が一定回行われなければkickする
            if not_accepted_count == KICK_THRESHOLD {
                not_accepted_count = 0;
                if rng.gen_bool(PROB_DOUBLE_BRIDGE) {
                    // double-bridge (4-optの1つ)
                    double_bridge_count += 1;
                    // (u,next_u),(v,next_v),(w,next_w),(x,next_x)を以下のように繋ぎ変える
                    // (u,next_w),(x,next_v),(w,next_u),(v,next_x)
                    let u = rng.gen_usize(0, state.path.len() - 4);
                    let v = rng.gen_usize(u + 1, state.path.len() - 3);
                    let w = rng.gen_usize(v + 1, state.path.len() - 2);
                    let x = rng.gen_usize(w + 1, state.path.len() - 1);

                    let i0 = state.path[u];
                    let i1 = state.path[u + 1];
                    let i2 = state.path[v];
                    let i3 = state.path[v + 1];
                    let i4 = state.path[w];
                    let i5 = state.path[w + 1];
                    let i6 = state.path[x];
                    let i7 = state.path[x + 1];

                    let mut original_score = state.calc_score(&input, i0, i1);
                    original_score += state.calc_score(&input, i2, i3);
                    original_score += state.calc_score(&input, i4, i5);
                    original_score += state.calc_score(&input, i6, i7);

                    // double-bridge
                    // u -> w+1
                    let mut kick_score = state.calc_score(&input, i0, i5);
                    // x -> v+1
                    kick_score += state.calc_score(&input, i6, i3);
                    // w -> u+1
                    kick_score += state.calc_score(&input, i4, i1);
                    // v -> x+1
                    kick_score += state.calc_score(&input, i2, i7);

                    let new_score = current_score + (kick_score - original_score);

                    // kick近傍は必ず更新
                    current_score = new_score;
                    // 採用された交換のtypeによって,pathの変え方を分岐する
                    let part1 = state.path[u + 1..v + 1].to_vec();
                    let part2 = state.path[v + 1..w + 1].to_vec();
                    let part3 = state.path[w + 1..x + 1].to_vec();
                    // path[0..u,u+1..v,v+1..w,w+1..x,x+1..0]をpath[0..u,w+1..x,v+1..w,u+1..v,x+1..0]としたい
                    state.path.splice(
                        u + 1..x + 1,
                        part3
                            .iter()
                            .cloned()
                            .chain(part2.iter().cloned().chain(part1.iter().cloned())),
                    );
                } else {
                    // 坂道輪サーチ
                    rin_sakamichi_count += 1;
                    state = best_state.clone();
                    current_score = best_score;
                }
                is_select_2opt = true;
            }
        }
        eprintln!("=== annealing ===");
        eprintln!("total iter: {}", total_iter);
        eprintln!("each iter: {}", each_iter);
        eprintln!("accepted: {}", accepted_count);
        eprintln!("update best: {}", update_best_count);
        eprintln!("kicked by double bridge: {}", double_bridge_count);
        eprintln!("kicked by rin sakamichi search: {}", rin_sakamichi_count);
        eprintln!("energy: {}", best_score);
        eprintln!(
            "score: {}",
            (1e9 / (1e3 + (best_score as f64).sqrt())).round() as i32
        );
        eprintln!("time: {}", get_time());
        eprintln!();
    }
    let solution = restore_solution(&input, &best_state);
    solution.print();
    let final_score = solution.calc_score_all(&input);
    eprintln!(
        "final score: {}",
        (1e9 / (1e3 + (final_score as f64).sqrt())).round() as i32
    );
    eprintln!("time: {}", get_time());
}
0