@@ -3,44 +3,37 @@ defmodule AdventOfCode.Y2015.Day13 do
33 --- Day 13: Knights of the Dinner Table ---
44 Problem Link: https://adventofcode.com/2015/day/13
55 Difficulty: s
6- Tags: brute-force combinatorics slow
6+ Tags: graph dynamic-programming
77 """
88 alias AdventOfCode.Helpers . { InputReader , Transformers }
9-
10- alias AdventOfCode.Algorithms.Combinatorics
9+ alias Yog.Model
10+ import Bitwise
1111
1212 def input , do: InputReader . read_from_file ( 2015 , 13 )
1313
1414 def run ( input \\ input ( ) ) do
15- { people , facts } = parse ( input )
15+ graph = parse ( input )
1616
1717 {
18- maximize_happiness ( { people , facts } ) ,
19- happiness_with_me ( { people , facts } )
18+ solve ( graph ) ,
19+ solve ( add_me ( graph ) )
2020 }
2121 end
2222
2323 def parse ( data \\ input ( ) ) do
24- facts =
24+ parsed =
2525 data
2626 |> Transformers . lines ( )
2727 |> Enum . map ( & parse_fact / 1 )
28- |> Enum . group_by ( & elem ( & 1 , 0 ) , & elem ( & 1 , 1 ) )
29- |> Enum . flat_map ( fn { mapset , [ happiness_1 , happiness_2 ] } ->
30- [ a , b ] = MapSet . to_list ( mapset )
31- happiness = happiness_1 + happiness_2
3228
33- [ { { a , b } , happiness } , { { b , a } , happiness } ]
29+ graph =
30+ Enum . reduce ( parsed , Yog . undirected ( ) , fn { a , b , _w } , g ->
31+ g |> Model . add_node ( a , nil ) |> Model . add_node ( b , nil )
3432 end )
35- |> Enum . into ( % { } )
36-
37- people =
38- facts
39- |> Map . keys ( )
40- |> Enum . flat_map ( & Tuple . to_list / 1 )
41- |> Enum . uniq ( )
4233
43- { people , facts }
34+ Enum . reduce ( parsed , graph , fn { a , b , w } , g ->
35+ Model . add_edge_with_combine! ( g , a , b , w , & Kernel . + / 2 )
36+ end )
4437 end
4538
4639 @ regex ~r/ (\w +) would (gain|lose) (\d +) happiness units by sitting next to (\w +)\. /
@@ -49,48 +42,67 @@ defmodule AdventOfCode.Y2015.Day13 do
4942 |> Regex . run ( statement , capture: :all_but_first )
5043 |> then ( fn [ person_a , action , value , person_b ] ->
5144 {
52- MapSet . new ( [ person_a , person_b ] ) ,
53- String . to_integer ( value ) * ( ( action == "gain" && 1 ) || - 1 )
45+ person_a ,
46+ person_b ,
47+ String . to_integer ( value ) * if ( action == "gain" , do: 1 , else: - 1 )
5448 }
5549 end )
5650 end
5751
58- defp happiness_with_me ( { people , facts } ) do
59- facts =
60- people
61- |> Enum . flat_map ( fn person ->
62- [
63- { { "Me" , person } , 0 } ,
64- { { person , "Me" } , 0 }
65- ]
66- end )
67- |> Enum . into ( % { } )
68- |> Map . merge ( facts )
52+ defp add_me ( graph ) do
53+ people = Model . all_nodes ( graph )
6954
70- people = [ "Me" | people ]
71-
72- maximize_happiness ( { people , facts } )
55+ Enum . reduce ( people , graph , fn person , g ->
56+ Model . add_edge_ensure ( g , "Me" , person , 0 )
57+ end )
7358 end
7459
75- defp maximize_happiness ( { people , facts } ) do
76- people
77- |> all_pairs ( )
78- |> Enum . map ( & collect_happiness ( & 1 , facts ) )
79- |> Enum . max ( )
60+ defp solve ( graph ) do
61+ nodes = Model . all_nodes ( graph )
62+ [ root | _rest ] = nodes
63+ nodes_indexed = Enum . with_index ( nodes )
64+ node_to_idx = Map . new ( nodes_indexed )
65+ _idx_to_node = Map . new ( Enum . map ( nodes_indexed , fn { n , i } -> { i , n } end ) )
66+
67+ weights =
68+ for { u , i } <- nodes_indexed , { v , j } <- nodes_indexed , into: % { } do
69+ { { i , j } , edge_weight ( graph , u , v ) }
70+ end
71+
72+ root_idx = node_to_idx [ root ]
73+ num_nodes = length ( nodes )
74+ target_mask = ( 1 <<< num_nodes ) - 1
75+
76+ Process . put ( :tsp_memo , % { } )
77+ tsp ( root_idx , 1 <<< root_idx , root_idx , target_mask , weights , num_nodes )
8078 end
8179
82- defp all_pairs ( people ) do
83- people
84- |> Combinatorics . permutations ( )
85- |> Enum . flat_map ( fn [ first | _ ] = people ->
86- [ people ++ [ first ] ]
87- end )
88- |> Enum . map ( & Enum . chunk_every ( & 1 , 2 , 1 , :discard ) )
80+ defp tsp ( curr_idx , mask , root_idx , target_mask , weights , n ) do
81+ if mask == target_mask do
82+ weights [ { curr_idx , root_idx } ]
83+ else
84+ memo = Process . get ( :tsp_memo )
85+
86+ if Map . has_key? ( memo , { curr_idx , mask } ) do
87+ memo [ { curr_idx , mask } ]
88+ else
89+ res =
90+ for next_idx <- 0 .. ( n - 1 ) , ( mask &&& 1 <<< next_idx ) == 0 do
91+ weights [ { curr_idx , next_idx } ] +
92+ tsp ( next_idx , bor ( mask , 1 <<< next_idx ) , root_idx , target_mask , weights , n )
93+ end
94+ |> Enum . max ( )
95+
96+ Process . put ( :tsp_memo , Map . put ( memo , { curr_idx , mask } , res ) )
97+ res
98+ end
99+ end
89100 end
90101
91- defp collect_happiness ( data , facts ) do
92- Enum . reduce ( data , 0 , fn [ person_1 , person_2 ] , happiness ->
93- happiness + facts [ { person_1 , person_2 } ]
94- end )
102+ defp edge_weight ( graph , u , v ) do
103+ case Enum . find ( Model . successors ( graph , u ) , fn { id , _ } -> id == v end ) do
104+ { _ , w } -> w
105+ nil -> 0
106+ end
95107 end
96108end
0 commit comments