結果

問題 No.5014 セクスタプル (reactive)
ユーザー r3yoheir3yohei
提出日時 2023-06-11 12:59:37
言語 Rust
(1.77.0 + proconio)
結果
AC  
実行時間 1,947 ms / 2,000 ms
コード長 11,002 bytes
コンパイル時間 2,609 ms
コンパイル使用メモリ 167,504 KB
実行使用メモリ 24,504 KB
スコア 522,163,382
平均クエリ数 35.00
最終ジャッジ日時 2023-06-11 13:02:54
合計ジャッジ時間 194,969 ms
ジャッジサーバーID
(参考情報)
judge13 / judge11
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1,841 ms
23,832 KB
testcase_01 AC 1,846 ms
23,652 KB
testcase_02 AC 1,859 ms
23,664 KB
testcase_03 AC 1,845 ms
23,436 KB
testcase_04 AC 1,837 ms
24,348 KB
testcase_05 AC 1,858 ms
23,544 KB
testcase_06 AC 1,839 ms
24,336 KB
testcase_07 AC 1,842 ms
23,856 KB
testcase_08 AC 1,852 ms
24,048 KB
testcase_09 AC 1,833 ms
23,376 KB
testcase_10 AC 1,814 ms
23,988 KB
testcase_11 AC 1,838 ms
23,544 KB
testcase_12 AC 1,851 ms
23,556 KB
testcase_13 AC 1,855 ms
23,652 KB
testcase_14 AC 1,834 ms
23,556 KB
testcase_15 AC 1,847 ms
23,556 KB
testcase_16 AC 1,853 ms
23,556 KB
testcase_17 AC 1,849 ms
24,324 KB
testcase_18 AC 1,873 ms
23,400 KB
testcase_19 AC 1,823 ms
24,348 KB
testcase_20 AC 1,855 ms
24,312 KB
testcase_21 AC 1,836 ms
23,424 KB
testcase_22 AC 1,890 ms
23,676 KB
testcase_23 AC 1,847 ms
23,388 KB
testcase_24 AC 1,818 ms
24,048 KB
testcase_25 AC 1,859 ms
23,568 KB
testcase_26 AC 1,854 ms
23,544 KB
testcase_27 AC 1,835 ms
23,556 KB
testcase_28 AC 1,853 ms
23,664 KB
testcase_29 AC 1,862 ms
23,640 KB
testcase_30 AC 1,849 ms
24,336 KB
testcase_31 AC 1,849 ms
23,388 KB
testcase_32 AC 1,840 ms
23,556 KB
testcase_33 AC 1,842 ms
23,640 KB
testcase_34 AC 1,864 ms
23,676 KB
testcase_35 AC 1,832 ms
23,676 KB
testcase_36 AC 1,855 ms
23,388 KB
testcase_37 AC 1,830 ms
24,000 KB
testcase_38 AC 1,846 ms
24,384 KB
testcase_39 AC 1,864 ms
24,360 KB
testcase_40 AC 1,849 ms
23,628 KB
testcase_41 AC 1,876 ms
23,556 KB
testcase_42 AC 1,855 ms
23,688 KB
testcase_43 AC 1,855 ms
23,412 KB
testcase_44 AC 1,843 ms
23,532 KB
testcase_45 AC 1,854 ms
23,544 KB
testcase_46 AC 1,833 ms
24,420 KB
testcase_47 AC 1,830 ms
24,264 KB
testcase_48 AC 1,816 ms
23,436 KB
testcase_49 AC 1,826 ms
24,276 KB
testcase_50 AC 1,842 ms
23,544 KB
testcase_51 AC 1,823 ms
24,372 KB
testcase_52 AC 1,839 ms
23,892 KB
testcase_53 AC 1,838 ms
24,012 KB
testcase_54 AC 1,849 ms
24,036 KB
testcase_55 AC 1,947 ms
24,048 KB
testcase_56 AC 1,854 ms
23,412 KB
testcase_57 AC 1,850 ms
23,412 KB
testcase_58 AC 1,844 ms
23,400 KB
testcase_59 AC 1,848 ms
23,844 KB
testcase_60 AC 1,873 ms
24,036 KB
testcase_61 AC 1,856 ms
24,276 KB
testcase_62 AC 1,839 ms
23,688 KB
testcase_63 AC 1,844 ms
24,396 KB
testcase_64 AC 1,856 ms
24,384 KB
testcase_65 AC 1,855 ms
24,048 KB
testcase_66 AC 1,847 ms
23,844 KB
testcase_67 AC 1,840 ms
23,628 KB
testcase_68 AC 1,845 ms
24,012 KB
testcase_69 AC 1,834 ms
23,676 KB
testcase_70 AC 1,863 ms
24,012 KB
testcase_71 AC 1,837 ms
24,036 KB
testcase_72 AC 1,852 ms
23,436 KB
testcase_73 AC 1,863 ms
23,640 KB
testcase_74 AC 1,842 ms
23,832 KB
testcase_75 AC 1,828 ms
24,504 KB
testcase_76 AC 1,852 ms
23,832 KB
testcase_77 AC 1,846 ms
23,436 KB
testcase_78 AC 1,835 ms
23,400 KB
testcase_79 AC 1,826 ms
23,400 KB
testcase_80 AC 1,825 ms
24,024 KB
testcase_81 AC 1,868 ms
24,048 KB
testcase_82 AC 1,836 ms
24,036 KB
testcase_83 AC 1,842 ms
24,060 KB
testcase_84 AC 1,839 ms
24,276 KB
testcase_85 AC 1,859 ms
23,628 KB
testcase_86 AC 1,842 ms
24,024 KB
testcase_87 AC 1,851 ms
23,424 KB
testcase_88 AC 1,834 ms
24,276 KB
testcase_89 AC 1,850 ms
24,048 KB
testcase_90 AC 1,853 ms
23,652 KB
testcase_91 AC 1,834 ms
23,556 KB
testcase_92 AC 1,859 ms
23,436 KB
testcase_93 AC 1,819 ms
24,048 KB
testcase_94 AC 1,820 ms
23,388 KB
testcase_95 AC 1,848 ms
23,652 KB
testcase_96 AC 1,853 ms
23,532 KB
testcase_97 AC 1,867 ms
23,640 KB
testcase_98 AC 1,829 ms
24,360 KB
testcase_99 AC 1,868 ms
23,400 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#![allow(non_snake_case, unused)]

use std::io::*;
use std::{collections::*, fmt::format};
use std::{cmp::*, vec};
use crate::rand::Xoshiro256;

const TURN: usize = 35; // 操作回数
const N: usize = 36; // セルの数 (=操作回数-1)
const PLAYOUT_EPOCH: usize = 10; // 期待値計算のためにプレイアウトする回数
const NUM_DICE: usize = 6; // 操作1回当りのダイスの数
const BOARD_SIZE: usize = 6; // ダイスを配置するセルのサイズ
const TL: f64 = 1.98; // 全体の制限時間

// 入力を受け取る
fn read_buffer() -> Vec<usize> {
    let mut input = String::new();
    std::io::stdin().read_line(&mut input).expect("Failed to read line.");
    let v: Vec<&str> = input.split_whitespace().collect();
    return v.iter().map(|&s| s.parse().unwrap()).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_i64(&mut self, lower: i64, upper: i64) -> i64 {
            assert!(lower < upper);
            let count = upper - lower;
            (self.next() % count as u64) as i64 + 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
    }
}

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

#[derive(Clone, Debug)]
struct State {
    turn: usize,
    dice: Vec<Vec<usize>>, // dice[i][j] := i回目のダイスjの出目
    cell: Vec<usize>, // cell[i * BOARD_SIZE + j] := i行j列目に置かれたのが何操作目の出目か
}
impl State {
    fn new() -> Self {
        let cell = vec![!0; BOARD_SIZE * BOARD_SIZE];
        Self { turn: 0, dice: vec![], cell }
    }
    // 入力から受け取ったダイスをp(0~35)のいずれかの位置に置く
    fn place(&mut self, p: usize, input: &Vec<usize>) -> bool {
        // 置けるかを判定する
        if self.cell[p] != !0 {return false;}
        // cell配列で保持する操作はdice配列へのアクセスを容易にするために0-indexとしておくので,turnのインクリメントより先に行う
        self.cell[p] = self.turn;
        self.turn += 1;
        self.dice.push(input.to_vec());
        true
    }
    fn calc_score(&self) -> i64 {
        let mut score = 0;
        for i in 0..BOARD_SIZE {
            // iを行,jを列とみなすのと,iを列,jを行とみなすのを同時にやる
            // iを行とみなすとき,i行目について,全ての列に登場する目があるか・全ての列に登場する目で,さらに追加の目があるかを調べる
            let mut num_dice_row = vec![0; NUM_DICE];
            let mut forbidden_set_row = HashSet::new();
            // iを列とみなすとき,i列目について,全ての行に登場する目があるか・全ての行に登場する目で,さらに追加の目があるかを調べる
            let mut num_dice_col = vec![0; NUM_DICE];
            let mut forbidden_set_col = HashSet::new();
            for j in 0..BOARD_SIZE {
                // iを行とみなすとき
                let dice_ij: &Vec<usize> = &self.dice[self.cell[i * BOARD_SIZE + j]];
                let mut num_dice_map_row = HashMap::new();
                for &d in dice_ij {
                    *num_dice_map_row.entry(d).or_insert(0) += 1;
                }
                // セル(i, j)について計上
                for d in 1..=6 {
                    // (i, j)に存在しない目があるかどうかを判定しておく
                    if let Some(&num_d) = num_dice_map_row.get(&d) {
                        // あればとりあえず計上
                        num_dice_row[d - 1] += num_d;
                    } else {
                        // なければ無いことを記録
                        forbidden_set_row.insert(d);
                    }
                }

                // iを列とみなすとき
                let dice_ji = &self.dice[self.cell[j * BOARD_SIZE + i]];
                let mut num_dice_map_col = HashMap::new();
                for &d in dice_ji {
                    *num_dice_map_col.entry(d).or_insert(0) += 1;
                }
                // セル(i, j)について計上
                for d in 1..=6 {
                    // (i, j)に存在しない目があるかどうかを判定しておく
                    if let Some(&num_d) = num_dice_map_col.get(&d) {
                        // あればとりあえず計上
                        num_dice_col[d - 1] += num_d;
                    } else {
                        // なければ無いことを記録
                        forbidden_set_col.insert(d);
                    }
                }
            }
            // iを行とみなすとき,各目1~6で,すべての列に含まれるものの数を数え,定義よりその数-3が当該行のスコアとなる
            // iを列とみなすときも同じ
            for d in 1..=6 {
                if !forbidden_set_row.contains(&d) {
                    score += num_dice_row[d - 1] - 3;
                }
                if !forbidden_set_col.contains(&d) {
                    score += num_dice_col[d - 1] - 3;
                }
            }
        }
        score
    }
}

// 現在の状態をtとして,t+1~最後まで遊んだ時のスコアの期待値が最も高いt+1番目の手を返す
// 現在入力から受け取ったダイスより先のダイスをt+1~TURN個勝手に決めてプレイしまくる
// t+1番目がLの手の平均スコア, t+1がR,...を計算し,最も高いものをt+1番目の手として採用する
fn playout(mut rng: &mut Xoshiro256, state: &State, input: &Vec<usize>) -> usize {
    // t番目の手とスコアの期待値のmap
    let mut map: HashMap<usize, i64> = HashMap::new();
    // t番目の手を固定して,t+1~36までを何回か遊ぶ
    let mut empty = vec![];
    // 置ける場所を探す
    for p in 0..N {
        if state.cell[p] == !0 {empty.push(p);}
    }
    // [ToDo]: 置ける場所の数に応じて,プレイアウトする回数を決める
    let playout_epoch = 60;
    for &p in &empty {
        // 各シミュレーションでのスコアを格納する(後で平均を出す)
        let mut scores = vec![];
        
        for _ in 0..playout_epoch {
            // まずはt番目は固定した手を実行
            let mut state_tmp = state.clone();
            state_tmp.place(p, input);

            // 残りターンでダイスを適当に生成し,置くことを繰り返す
            let crt_turn = state_tmp.turn;
            for t in crt_turn..=TURN {
                let mut sim_empty = vec![];
                // 置ける場所を探す
                for q in 0..N {
                    if state_tmp.cell[q] == !0 {sim_empty.push(q);}
                }
                // [ToDo]: ここで乱数が偏ると,置く場所や置く6つのダイスの目が偏ってしまう
                // UCB的な機構を入れたい
                let sim_p_idx = rng.gen_usize(0, sim_empty.len());
                let sim_p = sim_empty[sim_p_idx];
                let mut sim_input = vec![];
                for _ in 0..NUM_DICE {
                    sim_input.push(rng.gen_usize(1, 7));
                }
                state_tmp.place(sim_p, &sim_input);
            }
            // 終わったらスコア計算
            let score = state_tmp.calc_score();
            scores.push(score);
        }
        let mean = scores.iter().sum::<i64>() / scores.len() as i64;
        map.insert(p, mean);
    }
    // mapをソートして最大を取得
    let (p_max, _) = map.iter()
        .max_by(|a, b| a.1.cmp(&b.1))
        .unwrap();
    *p_max
}

fn main() {
    get_time();
    let mut rng = Xoshiro256::new(8192);
    let mut state = State::new();
    
    for t in 0..TURN {
        // 入力のダイスを受け取る
        let input = read_buffer();
        // [ToDo]: 同じ目がたくさんあるなら真ん中の方に置きたい
        // ランダムプレイアウトして,t番目の手を決める
        let p = playout(&mut rng, &state, &input);
        state.place(p, &input);
        // eprintln!("action: {}", p);
        // eprintln!("time: {}", get_time());
        // 回答を出力し,フラッシュする
        println!("{} {}", p / BOARD_SIZE + 1, p % BOARD_SIZE + 1);
        stdout().flush().unwrap();
    }

    eprintln!("=== Monte Carlo tree search ===");
    eprintln!("time: {}", get_time());
}
0