提出 #43651238


ソースコード 拡げる

#![allow(non_snake_case)]


fn main(){
    let input=Input::input();
    let node=Node{
        track_id:!0,
        state:[0;N],
        hash:0,
        score:(0..N).map(|i|input.score[0][i][0]).sum(),
    };
    let mut solver=BeamSearch::new(node);
    let ans=solver.solve(&input);

    for n in ans{
        if n==1{
            println!("A");
        } else {
            println!("B");
        }
    }
}


const N:usize=20;
use proconio::{*,marker::*};


struct Input{
    cmds:[[usize;3];TURN],
    score:[[[i16;TURN];N];TURN+1],
    zob:[u64;N],
    // zob:[[u64;TURN*2+1];N],
}
impl Input{
    fn input()->Input{
        input!{
            turn:usize,
            i_cmds:[[Usize1;3];TURN],
        }
        assert_eq!(turn,TURN);
        let mut cmds=[[!0;3];TURN];
        for i in 0..TURN{
            for j in 0..3{
                cmds[i][j]=i_cmds[i][j];
            }
        }
        
        let mut score=[[[0;TURN];N];TURN+1];

        for i in (0..TURN).rev(){
            for &j in &cmds[i]{
                score[i][j][0]=score[i+1][j][1];
                for k in 1..TURN{
                    score[i][j][k]=score[i+1][j][k-1]+(k==1) as i16;
                }
            }

            for j in 0..N{
                if cmds[i].contains(&j){
                    continue;
                }
                for k in 0..TURN{
                    score[i][j][k]=score[i+1][j][k]+(k==0) as i16;
                }
            }
        }

        let mut zob=[!0;N];
        use rand::prelude::*;
        let mut rng=rand_pcg::Pcg64Mcg::new(0);
        for i in 0..N{
            zob[i]=rng.gen();
        }

        Input{cmds,score,zob}
    }
}


#[allow(non_camel_case_types)]
type uint=u32;


#[derive(Clone)]
struct Node{
    track_id:uint,
    score:i16,
    hash:u64,
    state:[i8;N],
}
impl Node{
    fn new_node(&self,input:&Input,turn:usize,cand:&Cand)->Node{
        let mut state=self.state.clone();
        for &n in &input.cmds[turn]{
            state[n]+=cand.op;
        }
        
        Node{
            track_id:!0,
            score:cand.eval_score,
            hash:cand.hash,
            state
        }
    }
}


#[derive(Clone)]
struct Cand{
    op:i8,
    parent:uint,
    eval_score:i16,
    hash:u64,
}
impl Cand {
    fn raw_score(&self,input:&Input)->i16{
        self.eval_score
    }
}


const MAX_WIDTH:usize=100000;
const TURN:usize=100;


struct BeamSearch{
    track:Vec<(uint,i8)>,
    nodes:Vec<Node>,
    next_nodes:Vec<Node>,
}
impl BeamSearch{
    fn new(node:Node)->BeamSearch{
        const MAX_NODES:usize=MAX_WIDTH*TURN;
        // assert!(MAX_NODES<uint::MAX as usize,"MAX_NODEが足りないからuintのサイズを大きくしてね");
        let mut nodes=Vec::with_capacity(MAX_WIDTH);
        nodes.push(node);
        
        BeamSearch{
            nodes,
            track:Vec::with_capacity(MAX_NODES),
            next_nodes:Vec::with_capacity(MAX_WIDTH),
        }
    }
    
    fn enum_cands(&self,input:&Input,turn:usize,cands:&mut Vec<Cand>){
        for i in 0..self.nodes.len(){
            self.append_cands(input,turn,i,cands);
        }
    }
    
    fn update<I:Iterator<Item=Cand>>(&mut self,input:&Input,turn:usize,cands:I){
        self.next_nodes.clear();
        for cand in cands{
            let node=&self.nodes[cand.parent as usize];
            let mut new=node.new_node(input,turn,&cand);
            self.track.push((node.track_id,cand.op));
            new.track_id=self.track.len() as uint-1;
            self.next_nodes.push(new);
        }
        
        std::mem::swap(&mut self.nodes,&mut self.next_nodes);
    }
    
    fn restore(&self,mut idx:uint)->Vec<i8>{
        idx=self.nodes[idx as usize].track_id;
        let mut ret=vec![];
        while idx!=!0{
            ret.push(self.track[idx as usize].1);
            idx=self.track[idx as usize].0;
        }
        ret.reverse();
        ret
    }

    fn append_cands(&self,input:&Input,turn:usize,idx:usize,cands:&mut Vec<Cand>){
        let node=&self.nodes[idx];

        let next=|add:i8|->(i16,u64){
            let mut score=node.score;
            let mut hash=node.hash;
            for &n in &input.cmds[turn]{
                score-=input.score[turn][n][node.state[n].abs() as usize];
                let new=node.state[n]+add;
                hash+=add as u64*input.zob[n];
                score+=input.score[turn+1][n][new.abs() as usize];
                score+=(new==0) as i16;
            }
            (score,hash)
        };

        let (eval_score,hash)=next(1);
        let cand=Cand{
            op:1,
            parent:idx as uint,
            eval_score,hash,
        };
        cands.push(cand);

        if turn!=0{
            let (eval_score,hash)=next(-1);
            let cand=Cand{
                op:-1,
                parent:idx as uint,
                eval_score,hash,
            };
            cands.push(cand);
        }
    }
    
    fn solve(&mut self,input:&Input)->Vec<i8>{
        use std::cmp::Reverse;
        let M=MAX_WIDTH;
        
        let mut cands=Vec::<Cand>::with_capacity(MAX_WIDTH);
        let mut set=NopHashSet::default();
        for t in 0..TURN{
            if t!=0{
                if cands.len()>M{
                    cands.select_nth_unstable_by_key(M,|a|Reverse(a.eval_score));
                    cands.truncate(M);
                }
                cands.sort_unstable_by_key(|a|Reverse(a.eval_score));
                set.clear();
                self.update(input,t-1,cands.iter().filter(|cand|
                    set.insert(cand.hash)
                ).take(M).cloned());
            }

            cands.clear();
            self.enum_cands(input,t,&mut cands);
            assert!(!cands.is_empty(),"次の合法手が存在しないよ");
        }
        let best=cands.iter().max_by_key(|a|a.raw_score(input)).unwrap();
        eprintln!("score = {}",best.raw_score(input));

        let mut ret=self.restore(best.parent);
        ret.push(best.op);

        ret
    }
}


#[allow(unused)]
mod nop_hash{
    use std::collections::{HashMap,HashSet};
    use core::hash::BuildHasherDefault;
    use core::hash::Hasher;
    
    #[derive(Default)]
    pub struct NopHasher{
        hash:u64,
    }
    impl Hasher for NopHasher{
        fn write(&mut self,_:&[u8]){
            panic!();
        }
    
        #[inline]
        fn write_u64(&mut self,n:u64){
            self.hash=n;
        }
    
        #[inline]
        fn finish(&self)->u64{
            self.hash
        }
    }
    
    pub type NopHashMap<K,V>=HashMap<K,V,BuildHasherDefault<NopHasher>>;
    pub type NopHashSet<V>=HashSet<V,BuildHasherDefault<NopHasher>>;
}
#[allow(unused)]
use nop_hash::*;


#[allow(unused)]
mod nth{
    use std::cmp::{self, Ordering, Ordering::*};
    use std::mem::{self, MaybeUninit};
    use std::ptr;
    
    struct CopyOnDrop<T> {
        src: *const T,
        dest: *mut T,
    }
    
    impl<T> Drop for CopyOnDrop<T> {
        fn drop(&mut self) {
            unsafe {
                ptr::copy_nonoverlapping(self.src, self.dest, 1);
            }
        }
    }
    
    fn shift_tail<T, F>(v: &mut [T], is_less: &mut F)
    where
        F: FnMut(&T, &T) -> bool,
    {
        let len = v.len();
        unsafe {
            if len >= 2 && is_less(v.get_unchecked(len - 1), v.get_unchecked(len - 2)) {
                let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(len - 1)));
                let v = v.as_mut_ptr();
                let mut hole = CopyOnDrop { src: &*tmp, dest: v.add(len - 2) };
                ptr::copy_nonoverlapping(v.add(len - 2), v.add(len - 1), 1);
    
                for i in (0..len - 2).rev() {
                    if !is_less(&*tmp, &*v.add(i)) {
                        break;
                    }
    
                    ptr::copy_nonoverlapping(v.add(i), v.add(i + 1), 1);
                    hole.dest = v.add(i);
                }
            }
        }
    }
    
    fn insertion_sort<T, F>(v: &mut [T], is_less: &mut F)
    where
        F: FnMut(&T, &T) -> bool,
    {
        for i in 1..v.len() {
            shift_tail(&mut v[..i + 1], is_less);
        }
    }
    
    fn partition_in_blocks<T, F>(v: &mut [T], pivot: &T, is_less: &mut F) -> usize
    where
        F: FnMut(&T, &T) -> bool,
    {
        const BLOCK: usize = 128;
    
        let mut l = v.as_mut_ptr();
        let mut block_l = BLOCK;
        let mut start_l = ptr::null_mut();
        let mut end_l = ptr::null_mut();
        let mut offsets_l = [MaybeUninit::<u8>::uninit(); BLOCK];
    
        let mut r = unsafe { l.add(v.len()) };
        let mut block_r = BLOCK;
        let mut start_r = ptr::null_mut();
        let mut end_r = ptr::null_mut();
        let mut offsets_r = [MaybeUninit::<u8>::uninit(); BLOCK];
    
        fn width<T>(l: *mut T, r: *mut T) -> usize {
            assert!(mem::size_of::<T>() > 0);
            (r as usize - l as usize) / mem::size_of::<T>()
        }
    
        loop {
            let is_done = width(l, r) <= 2 * BLOCK;
    
            if is_done {
                let mut rem = width(l, r);
                if start_l < end_l || start_r < end_r {
                    rem -= BLOCK;
                }
    
                if start_l < end_l {
                    block_r = rem;
                } else if start_r < end_r {
                    block_l = rem;
                } else {
                    block_l = rem / 2;
                    block_r = rem - block_l;
                }
            }
    
            if start_l == end_l {
                start_l = offsets_l.as_mut_ptr() as *mut _;
                end_l = start_l;
                let mut elem = l;
    
                for i in 0..block_l {
                    unsafe {
                        *end_l = i as u8;
                        end_l = end_l.offset(!is_less(&*elem, pivot) as isize);
                        elem = elem.offset(1);
                    }
                }
            }
    
            if start_r == end_r {
                start_r = offsets_r.as_mut_ptr() as *mut _;
                end_r = start_r;
                let mut elem = r;
    
                for i in 0..block_r {
                    unsafe {
                        elem = elem.offset(-1);
                        *end_r = i as u8;
                        end_r = end_r.offset(is_less(&*elem, pivot) as isize);
                    }
                }
            }
    
            let count = cmp::min(width(start_l, end_l), width(start_r, end_r));
    
            if count > 0 {
                macro_rules! left {
                    () => {
                        l.offset(*start_l as isize)
                    };
                }
                macro_rules! right {
                    () => {
                        r.offset(-(*start_r as isize) - 1)
                    };
                }
    
                unsafe {
                    let tmp = ptr::read(left!());
                    ptr::copy_nonoverlapping(right!(), left!(), 1);
    
                    for _ in 1..count {
                        start_l = start_l.offset(1);
                        ptr::copy_nonoverlapping(left!(), right!(), 1);
                        start_r = start_r.offset(1);
                        ptr::copy_nonoverlapping(right!(), left!(), 1);
                    }
    
                    ptr::copy_nonoverlapping(&tmp, right!(), 1);
                    mem::forget(tmp);
                    start_l = start_l.offset(1);
                    start_r = start_r.offset(1);
                }
            }
    
            if start_l == end_l {
                l = unsafe { l.offset(block_l as isize) };
            }
    
            if start_r == end_r {
                r = unsafe { r.offset(-(block_r as isize)) };
            }
    
            if is_done {
                break;
            }
        }
    
        if start_l < end_l {
            while start_l < end_l {
                unsafe {
                    end_l = end_l.offset(-1);
                    ptr::swap(l.offset(*end_l as isize), r.offset(-1));
                    r = r.offset(-1);
                }
            }
            width(v.as_mut_ptr(), r)
        } else if start_r < end_r {
            while start_r < end_r {
                unsafe {
                    end_r = end_r.offset(-1);
                    ptr::swap(l, r.offset(-(*end_r as isize) - 1));
                    l = l.offset(1);
                }
            }
            width(v.as_mut_ptr(), l)
        } else {
            width(v.as_mut_ptr(), l)
        }
    }
    
    fn partition<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> (usize, bool)
    where
        F: FnMut(&T, &T) -> bool,
    {
        let (mid, was_partitioned) = {
            v.swap(0, pivot);
            let (pivot, v) = v.split_at_mut(1);
            let pivot = &mut pivot[0];
    
            let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) });
            let _pivot_guard = CopyOnDrop { src: &*tmp, dest: pivot };
            let pivot = &*tmp;
    
            let mut l = 0;
            let mut r = v.len();
    
            unsafe {
                while l < r && is_less(v.get_unchecked(l), pivot) {
                    l += 1;
                }
    
                while l < r && !is_less(v.get_unchecked(r - 1), pivot) {
                    r -= 1;
                }
            }
    
            (l + partition_in_blocks(&mut v[l..r], pivot, is_less), l >= r)
        };
    
        v.swap(0, mid);
    
        (mid, was_partitioned)
    }
    
    fn partition_equal<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> usize
    where
        F: FnMut(&T, &T) -> bool,
    {
        v.swap(0, pivot);
        let (pivot, v) = v.split_at_mut(1);
        let pivot = &mut pivot[0];
    
        let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) });
        let _pivot_guard = CopyOnDrop { src: &*tmp, dest: pivot };
        let pivot = &*tmp;
    
        let mut l = 0;
        let mut r = v.len();
        loop {
            unsafe {
                while l < r && !is_less(pivot, v.get_unchecked(l)) {
                    l += 1;
                }
    
                while l < r && is_less(pivot, v.get_unchecked(r - 1)) {
                    r -= 1;
                }
    
                if l >= r {
                    break;
                }
    
                r -= 1;
                let ptr = v.as_mut_ptr();
                ptr::swap(ptr.add(l), ptr.add(r));
                l += 1;
            }
        }
    
        l + 1
    }
    
    fn choose_pivot<T, F>(v: &mut [T], is_less: &mut F) -> (usize, bool)
    where
        F: FnMut(&T, &T) -> bool,
    {
        const SHORTEST_MEDIAN_OF_MEDIANS: usize = 50;
        const MAX_SWAPS: usize = 4 * 3;
    
        let len = v.len();
    
        let mut a = len / 4 * 1;
        let mut b = len / 4 * 2;
        let mut c = len / 4 * 3;
    
        let mut swaps = 0;
    
        if len >= 8 {
            let mut sort2 = |a: &mut usize, b: &mut usize| unsafe {
                if is_less(v.get_unchecked(*b), v.get_unchecked(*a)) {
                    ptr::swap(a, b);
                    swaps += 1;
                }
            };
    
            let mut sort3 = |a: &mut usize, b: &mut usize, c: &mut usize| {
                sort2(a, b);
                sort2(b, c);
                sort2(a, b);
            };
    
            if len >= SHORTEST_MEDIAN_OF_MEDIANS {
                let mut sort_adjacent = |a: &mut usize| {
                    let tmp = *a;
                    sort3(&mut (tmp - 1), a, &mut (tmp + 1));
                };
    
                sort_adjacent(&mut a);
                sort_adjacent(&mut b);
                sort_adjacent(&mut c);
            }
    
            sort3(&mut a, &mut b, &mut c);
        }
    
        if swaps < MAX_SWAPS {
            (b, swaps == 0)
        } else {
            v.reverse();
            (len - 1 - b, true)
        }
    }
    
    
    fn partition_at_index_loop<'a, T, F>(
        mut v: &'a mut [T],
        mut index: usize,
        is_less: &mut F,
        mut pred: Option<&'a T>,
    ) where
        F: FnMut(&T, &T) -> bool,
    {
        loop {
            const MAX_INSERTION: usize = 10;
            if v.len() <= MAX_INSERTION {
                insertion_sort(v, is_less);
                return;
            }
    
            let (pivot, _) = choose_pivot(v, is_less);
    
            if let Some(p) = pred {
                if !is_less(p, &v[pivot]) {
                    let mid = partition_equal(v, pivot, is_less);
    
                    if mid > index {
                        return;
                    }
    
                    v = &mut v[mid..];
                    index = index - mid;
                    pred = None;
                    continue;
                }
            }
    
            let (mid, _) = partition(v, pivot, is_less);
    
            let (left, right) = v.split_at_mut(mid);
            let (pivot, right) = right.split_at_mut(1);
            let pivot = &pivot[0];
    
            if mid < index {
                v = right;
                index = index - mid - 1;
                pred = Some(pivot);
            } else if mid > index {
                v = left;
            } else {
                return;
            }
        }
    }
    
    fn partition_at_index<T, F>(
        v: &mut [T],
        index: usize,
        mut is_less: F,
    ) -> (&mut [T], &mut T, &mut [T])
    where
        F: FnMut(&T, &T) -> bool,
    {
        use Greater;
        use Less;
    
        if index >= v.len() {
            panic!("partition_at_index index {} greater than length of slice {}", index, v.len());
        }
    
        if mem::size_of::<T>() == 0 {
        } else if index == v.len() - 1 {
            let (max_index, _) = v
                .iter()
                .enumerate()
                .max_by(|&(_, x), &(_, y)| if is_less(x, y) { Less } else { Greater })
                .unwrap();
            v.swap(max_index, index);
        } else if index == 0 {
            let (min_index, _) = v
                .iter()
                .enumerate()
                .min_by(|&(_, x), &(_, y)| if is_less(x, y) { Less } else { Greater })
                .unwrap();
            v.swap(min_index, index);
        } else {
            partition_at_index_loop(v, index, &mut is_less, None);
        }
    
        let (left, right) = v.split_at_mut(index);
        let (pivot, right) = right.split_at_mut(1);
        let pivot = &mut pivot[0];
        (left, pivot, right)
    }


    pub trait Nth{
        type T;
        fn select_nth_unstable(&mut self, index: usize) where Self::T: Ord;
        fn select_nth_unstable_by<F: FnMut(&Self::T, &Self::T) -> Ordering>(&mut self, index: usize, compare: F);
        fn select_nth_unstable_by_key<K: Ord, F: FnMut(&Self::T) -> K>(&mut self, index: usize, f: F);
    }

    impl<T> Nth for [T]{
        type T = T;

        #[inline]
        fn select_nth_unstable(&mut self, index: usize) where T: Ord{
            partition_at_index(self, index, T::lt);
        }

        #[inline]
        fn select_nth_unstable_by<F: FnMut(&T, &T) -> Ordering>(&mut self, index: usize, mut compare: F){
            partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less);
        }
        
        #[inline]
        fn select_nth_unstable_by_key<K: Ord, F: FnMut(&T) -> K>(&mut self, index:usize, mut f: F){
            partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b)));
        }
    }
}
#[allow(unused)]
use nth::*;

提出情報

提出日時
問題 A49 - Heuristic 2
ユーザ rhoo
言語 Rust (1.42.0)
得点 48822
コード長 20364 Byte
結果 AC
実行時間 776 ms
メモリ 78508 KiB

コンパイルエラー

warning: unused variable: `input`
   --> src/main.rs:117:24
    |
117 |     fn raw_score(&self,input:&Input)->i16{
    |                        ^^^^^ help: consider prefixing with an underscore: `_input`
    |
    = note: `#[warn(unused_variables)]` on by default

ジャッジ結果

セット名 All
得点 / 配点 48822 / 1000
結果
AC × 50
セット名 テストケース
All in01.txt, in02.txt, in03.txt, in04.txt, in05.txt, in06.txt, in07.txt, in08.txt, in09.txt, in10.txt, in11.txt, in12.txt, in13.txt, in14.txt, in15.txt, in16.txt, in17.txt, in18.txt, in19.txt, in20.txt, in21.txt, in22.txt, in23.txt, in24.txt, in25.txt, in26.txt, in27.txt, in28.txt, in29.txt, in30.txt, in31.txt, in32.txt, in33.txt, in34.txt, in35.txt, in36.txt, in37.txt, in38.txt, in39.txt, in40.txt, in41.txt, in42.txt, in43.txt, in44.txt, in45.txt, in46.txt, in47.txt, in48.txt, in49.txt, in50.txt
ケース名 結果 実行時間 メモリ
in01.txt AC 721 ms 77676 KiB
in02.txt AC 726 ms 78060 KiB
in03.txt AC 738 ms 77864 KiB
in04.txt AC 737 ms 78128 KiB
in05.txt AC 745 ms 77948 KiB
in06.txt AC 707 ms 77564 KiB
in07.txt AC 727 ms 78060 KiB
in08.txt AC 759 ms 78508 KiB
in09.txt AC 721 ms 77596 KiB
in10.txt AC 715 ms 78428 KiB
in11.txt AC 705 ms 77424 KiB
in12.txt AC 738 ms 78072 KiB
in13.txt AC 717 ms 77844 KiB
in14.txt AC 691 ms 77856 KiB
in15.txt AC 696 ms 77924 KiB
in16.txt AC 719 ms 77372 KiB
in17.txt AC 714 ms 77208 KiB
in18.txt AC 709 ms 77704 KiB
in19.txt AC 724 ms 77860 KiB
in20.txt AC 706 ms 77608 KiB
in21.txt AC 724 ms 77664 KiB
in22.txt AC 715 ms 77872 KiB
in23.txt AC 669 ms 78132 KiB
in24.txt AC 745 ms 77144 KiB
in25.txt AC 776 ms 77888 KiB
in26.txt AC 742 ms 78120 KiB
in27.txt AC 759 ms 77544 KiB
in28.txt AC 722 ms 77380 KiB
in29.txt AC 732 ms 77800 KiB
in30.txt AC 726 ms 77596 KiB
in31.txt AC 712 ms 78348 KiB
in32.txt AC 718 ms 77876 KiB
in33.txt AC 720 ms 77872 KiB
in34.txt AC 718 ms 77800 KiB
in35.txt AC 708 ms 78136 KiB
in36.txt AC 739 ms 77860 KiB
in37.txt AC 698 ms 78160 KiB
in38.txt AC 713 ms 78136 KiB
in39.txt AC 709 ms 77844 KiB
in40.txt AC 746 ms 77332 KiB
in41.txt AC 743 ms 77792 KiB
in42.txt AC 719 ms 78108 KiB
in43.txt AC 719 ms 77620 KiB
in44.txt AC 692 ms 78176 KiB
in45.txt AC 733 ms 78412 KiB
in46.txt AC 718 ms 77564 KiB
in47.txt AC 710 ms 77264 KiB
in48.txt AC 688 ms 77880 KiB
in49.txt AC 734 ms 77876 KiB
in50.txt AC 717 ms 78052 KiB
sample_01.txt RE 2 ms 2028 KiB