AOC 2025

软件工程 2025-08-21

您可以在此处阅读第5天的完整说明。

总而言之,第1部分,我们得到了许多堆栈,每个堆栈可能包含许多板条箱。而且,您必须应用移动操作,以将板条箱从一个堆栈移动到另一个堆栈,在所有堆栈完成移动操作后,我们需要找出每个堆栈顶部的板条箱结束。

为了解决这个问题,我们只需要使用堆栈数据结构来容纳我们的板条箱,以防万一:

  • 堆栈是最终的(LIFO)数据结构,它是一种抽象数据类型,通常由数组或列表实现。

  • push()操作将元素添加到堆栈顶部。

  • pop()操作删除了堆栈的最高元素。

但是首先,我们需要解析输入以获得初始板条设置以及移动操作,这是示例输入:

 [Q]         [N]             [N]    
[H]     [B] [D]             [S] [M]
[C]     [Q] [J]         [V] [Q] [D]
[T]     [S] [Z] [F]     [J] [J] [W]
[N] [G] [T] [S] [V]     [B] [C] [C]
[S] [B] [R] [W] [D] [J] [Q] [R] [Q]
[V] [D] [W] [G] [P] [W] [N] [T] [S]
[B] [W] [F] [L] [M] [F] [L] [G] [J]
 1   2   3   4   5   6   7   8   9 

move 3 from 6 to 2
move 2 from 8 to 7
move 3 from 3 to 8
move 2 from 5 to 3
move 5 from 9 to 7

为了使板条箱解析过程更容易,在这里,对于尚未完全填充的堆栈,我们将[-]板条箱添加到它们:

 [Q] [-] [-] [N] [-] [-] [-] [N] [-]
[H] [-] [B] [D] [-] [-] [-] [S] [M]
[C] [-] [Q] [J] [-] [-] [V] [Q] [D]
[T] [-] [S] [Z] [F] [-] [J] [J] [W]
[N] [G] [T] [S] [V] [-] [B] [C] [C]
[S] [B] [R] [W] [D] [J] [Q] [R] [Q]
[V] [D] [W] [G] [P] [W] [N] [T] [S]
[B] [W] [F] [L] [M] [F] [L] [G] [J]
 1   2   3   4   5   6   7   8   9

现在,我们准备解析板条箱设置,我们有9种不同的堆栈,因此理想情况下,我们希望这样的每个相关板条箱这样:

 public static Map <  Integer , Stack <  Character  > > getCrates ( String input ) {Map <  Integer , Stack <  Character  > > stacks = new HashMap <  >() ;List <  List <  String  > > lines = input                               . lines (). limit ( 8 ). map ( x - > Arrays . stream ( x . split ( " \ s+ " )). collect ( Collectors . toList ())). collect ( Collectors . toList ()) ;for ( int i = lines . size () - 1 ; i  >= 0 ; i -- ) {List <  String  > crates = lines . get ( i ) ;for ( int j = 0 ; j <  crates . size () ; j ++ ) {Character label = getLabel ( crates . get ( j )) ;if ( ! label . equals ( '  ' )) {if ( stacks . get ( j + 1 ) == null ) {Stack <  Character  > stack = new Stack <  >() ;stack . push ( label ) ;stacks . put ( j + 1 , stack ) ;} else {Stack <  Character  > stack = stacks . get ( j + 1 ) ;stack . push ( label ) ;}}		  }}return stacks ;}

接下来,我们解析将板条箱从一个堆栈移到另一个堆栈的操作,为此,我们创建一个代表此信息的对象:

 @ AllArgsConstructorclass Operation {     int moves ;     int from ;     int to ;}

在这里,我们有一个解析操作的功能:

 public static List <  Operation  > getOperations ( String input ) {List <  String  > lines = input. lines (). skip ( 10 ). collect ( Collectors . toList ()) ;List <  Operation  > operations = new ArrayList <  >() ;List <  Integer  > nums = new ArrayList <  >() ;for ( final var line : lines ) {String [] in = line . split ( " \ s+ " ) ;for ( final var i : in ) {char ch = i . charAt ( 0 ) ;if ( Character . isDigit ( ch )) {nums . add ( Integer . parseInt ( i )) ;}}operations . add ( new Operation ( nums . get ( 0 ), nums . get ( 1 ), nums . get ( 2 ))) ;nums . clear () ;}return operations ;}

对于第1部分,我们只需要迭代每个操作,对于每个操作,我们将一个板条箱从一个堆栈中依次将一个板条箱移到另一个板条箱之前:

 Map <  Integer , Stack <  Character  > > crates = getCrates ( input ) ;List <  Operation  > operations = getOperations ( input ) ;for ( final var op : operations ) {   for ( int i = 0 ; i <  op . moves ; i ++ ) {       Stack <  Character  > fromStack = crates . get ( op . from ) ;Character label = fromStack . pop () ;Stack <  Character  > toStack = crates . get ( op . to ) ;       toStack . push ( label ) ;   }}

最后,我们循环遍历每个堆栈以获取最上方的板条箱:

 StringBuilder sb = new StringBuilder () ;for ( final var stack : crates . values ()) {   if ( ! stack . isEmpty ()) {      sb . append ( stack . peek ()) ;   }}

对于第2部分,要求略有变化,而不是逆转板条箱顺序,现在我们必须保留移动前后板条箱的顺序。为此,我们只是将数据结构从堆栈更改为列表:

 Map <  Integer , List <  Character  > > crates = getCrates ( input ). entrySet (). stream (). collect ( Collectors . toMap ( Map . Entry :: getKey , x - > new ArrayList <  >( x . getValue ()))) ;

对于每个操作,我们将一个板条箱的子弹板从一个堆栈移到另一个堆栈。由于移动数的数量可能大于源堆栈的大小,因此我们需要谨慎:

 int left = from . size () - op . moves ;int right = left + op . moves ;List <  Character  > moves = from . subList ( Math . max ( 0 , left ), Math . min ( from . size (), right )) ;

然后,我们将Sublist从源堆栈添加到目标堆栈:

 List <  Character  > from = crates . get ( op . from ) ;List <  Character  > to = crates . get ( op . to ) ;to . addAll ( moves ) ;

由于源堆栈中仍有一些板条箱,我们需要使它们保持完整:

 List <  Character  > stay = from . subList ( 0 , Math . min ( from . size () - op . moves , from . size ())) ;crates . put ( op . from , stay ) ;

最后,我们可以通过查看列表中的最后一个元素来检查每个堆栈的最高板条箱,我们完成了:

 StringBuilder sb = new StringBuilder () ;for ( final var stack : crates . values ()) {   if ( ! stack . isEmpty ()) {      sb . append ( stack . get ( stack . size () - 1 )) ;   }}