호두나무 공방/Advent of Code

Advent of Code 2021 Day 7 in Elixir - The Treachery of Whales

2021. 12. 7. 21:53

문제 보기

여러 마리의 게(그런데 이제 얘네들이 잠수함에 탄)가 있을 때, 이 게들이 최소한의 에너지로 모일 수 있는 한 점과 그 때 필요한 연료의 양을 계산하는 문제였다. 어제자 문제의 교훈이 있어서 일찌감치 Enum.frequencies를 활용해서 계산했다.

2년 전에 처음 AoC에 도전할 때에는 너무 어려워서 힘들어했는데 이번에는 (적어도 지금까지는) 거의 모든 문제가 30분 남짓 하면 풀린다. 한 언어를 꾸준히 붙잡아 왔으니 실력이 조금은 늘었나 싶기도 한데, 그것보다 문제가 쉬워진 게 더 큰 듯. 저녁에 쉬면서 '오늘의 퍼즐' 푸는 감각으로 마음편히 할 수 있어서 개인적으로는 효능감이 느껴지는 난이도이긴 하다.

defmodule Day7 do
  def parse_input do
    path = "input/day7.txt"

    File.read!(path)
    |> String.split(",")
    |> Enum.map(&String.to_integer/1)
  end

  @spec run_q1 :: {non_neg_integer, non_neg_integer}
  def run_q1 do
    crabs = parse_input() |> Enum.frequencies()

    {min, max} = crabs |> Map.keys() |> Enum.min_max()

    min_fuel_pos =
      min..max
      |> Enum.min_by(&get_fuel_q1(crabs, &1))

    {min_fuel_pos, get_fuel_q1(crabs, min_fuel_pos)}
  end

  @spec run_q2 :: {non_neg_integer, non_neg_integer}
  def run_q2 do
    crabs = parse_input() |> Enum.frequencies()

    {min, max} = crabs |> Map.keys() |> Enum.min_max()

    min_fuel_pos =
      min..max
      |> Enum.min_by(&get_fuel_q2(crabs, &1))

    {min_fuel_pos, get_fuel_q2(crabs, min_fuel_pos)}
  end

  defp get_fuel_q1(crabs, to_pos) do
    crabs
    |> Enum.map(fn {crab_pos, crab_count} ->
      abs(crab_pos - to_pos) * crab_count
    end)
    |> Enum.sum()
  end

  defp get_fuel_q2(crabs, to_pos) do
    crabs
    |> Enum.map(fn {crab_pos, crab_count} ->
      dist = abs(crab_pos - to_pos)
      fuel_to_move = div(dist * (dist + 1), 2)
      fuel_to_move * crab_count
    end)
    |> Enum.sum()
  end
end