1+ use std:: cmp:: Ordering ;
12use utils:: prelude:: * ;
23
3- /// Sorting lists using a partial order of constraints.
4+ /// Sorting lists using constraints.
5+ ///
6+ /// The solution assumes that the rules form a total order for the elements in each sequence.
47#[ derive( Clone , Debug ) ]
58pub struct Day05 {
6- rules : Rules ,
9+ before : Rules ,
710 sorted : Vec < Vec < u32 > > ,
811 unsorted : Vec < Vec < u32 > > ,
912}
1013
1114const MIN_NUM : usize = 10 ;
1215const MAX_NUM : usize = 99 ;
1316const RANGE : usize = MAX_NUM - MIN_NUM + 1 ;
14- type Rules = [ bool ; RANGE * RANGE ] ;
17+ type Rules = [ [ bool ; RANGE ] ; RANGE ] ;
1518
1619impl Day05 {
1720 pub fn new ( input : & str , _: InputType ) -> Result < Self , InputError > {
1821 let num = parser:: number_range ( MIN_NUM as u32 ..=MAX_NUM as u32 ) ;
1922 let rules_parser = num. then ( num. with_prefix ( b'|' ) ) . repeat ( b'\n' , 1 ) ;
2023 let updates_parser = num. repeat ( b',' , 1 ) . repeat ( b'\n' , 1 ) ;
2124
22- let ( rules_list , updates) = rules_parser
25+ let ( rule_list , updates) = rules_parser
2326 . then ( updates_parser. with_prefix ( b'\n' ) )
2427 . parse_complete ( input) ?;
2528
26- let mut rules = [ false ; RANGE * RANGE ] ;
27- for ( a, b) in rules_list {
28- rules[ ( a as usize - MIN_NUM ) * RANGE + b as usize ] = true ;
29+ let mut before: Rules = [ [ false ; RANGE ] ; RANGE ] ;
30+ for ( a, b) in rule_list {
31+ if before[ a as usize - MIN_NUM ] [ b as usize - MIN_NUM ] {
32+ return Err ( InputError :: new ( input, 0 , "duplicate rule" ) ) ;
33+ } else if before[ b as usize - MIN_NUM ] [ a as usize - MIN_NUM ] {
34+ return Err ( InputError :: new ( input, 0 , "contradictory pair of rules" ) ) ;
35+ }
36+
37+ before[ a as usize - MIN_NUM ] [ b as usize - MIN_NUM ] = true ;
2938 }
3039
3140 let ( sorted, unsorted) = updates. into_iter ( ) . partition ( |update| {
32- for ( i, & page1) in update. iter ( ) . enumerate ( ) {
33- for & page2 in update. iter ( ) . skip ( i + 1 ) {
34- if Self :: must_be_before ( & rules, page2, page1) {
35- return false ;
36- }
37- }
38- }
39- true
41+ update. is_sorted_by ( |& a, & b| before[ a as usize - MIN_NUM ] [ b as usize - MIN_NUM ] )
4042 } ) ;
4143
4244 Ok ( Self {
43- rules ,
45+ before ,
4446 sorted,
4547 unsorted,
4648 } )
@@ -56,38 +58,24 @@ impl Day05 {
5658
5759 #[ must_use]
5860 pub fn part2 ( & self ) -> u32 {
59- let mut result = 0 ;
60- let mut list = Vec :: new ( ) ;
61- for original in & self . unsorted {
62- original. clone_into ( & mut list) ;
63-
64- for _ in 0 ..( list. len ( ) / 2 ) {
65- let i = Self :: find_first_index ( & self . rules , & list) ;
66- list. swap_remove ( i) ;
67- }
68-
69- let i = Self :: find_first_index ( & self . rules , & list) ;
70- result += list[ i] ;
71- }
72-
73- result
74- }
75-
76- fn must_be_before ( rules : & Rules , page1 : u32 , page2 : u32 ) -> bool {
77- rules[ ( page1 as usize - MIN_NUM ) * RANGE + page2 as usize ]
78- }
79-
80- fn find_first_index ( rules : & Rules , list : & [ u32 ] ) -> usize {
81- ' outer: for ( i, & page1) in list. iter ( ) . enumerate ( ) {
82- for ( j, & page2) in list. iter ( ) . enumerate ( ) {
83- if i != j && Self :: must_be_before ( rules, page2, page1) {
84- continue ' outer;
85- }
86- }
87-
88- return i;
89- }
90- panic ! ( "no solution found" ) ;
61+ self . unsorted
62+ . iter ( )
63+ . cloned ( )
64+ . map ( |mut update| {
65+ let index = update. len ( ) / 2 ;
66+ // Will panic if the provided rules are not a total order
67+ let ( _, middle, _) = update. select_nth_unstable_by ( index, |& a, & b| {
68+ if a == b {
69+ Ordering :: Equal
70+ } else if self . before [ a as usize - MIN_NUM ] [ b as usize - MIN_NUM ] {
71+ Ordering :: Less
72+ } else {
73+ Ordering :: Greater
74+ }
75+ } ) ;
76+ * middle
77+ } )
78+ . sum ( )
9179 }
9280}
9381
0 commit comments