사용자 도구

사이트 도구


ps:advent_of_code:2023

Advent Of Code 2023

  • 첫 참여.
  • 한국시간 오후 2시에 문제가 공개된다. 적절하다
  • 12/10 까지는 업무가 바빠서.. 문제가 공개되는 2시에 문제를 풀 여력이 전혀 없었다..
  • 12/11 부터는 휴일이 아니라면, 2시에 바로 문제를 푸는게 가능할것 같다
    • 12/11일 하루만 2시에 바로 문제풀기 성공. 그뒤에는 다시 계속 실패
  • 어쨌든 속도가 중요한 대회이므로, 코드를 깔끔히 하는것은 패스. 여기에 올리는 코드도 코드정리를 따로 안한 채로 올릴 예정.

Day 11: Cosmic Expansion

  • 풀이
    • 모든 갤럭시들에 대해서 좌표를 모두 계산해주고, 모든 페어에 대해서 거리를 계산해주는 O(n^2) 방법을 사용해도 시간상으로는 얼마 안걸릴거 같다. 갤럭시의 갯수가 O(r*c) 이니까, O(r^2*c^2)
    • 빈 행과 빈 열을 고려해서 좌표를 계산하는 것이 구현하기 조금 귀찮은 작업.
    • 좀더 효율적으로 하려면, x 거리의 합과 y 거리의 합으로 나누어 계산하고, 같은 행이나 같은 열에 들어있는 갤럭시들의 갯수를 세어주는 식으로 계산하면 O((r+c)^2) 까지는 줄일수 있을듯.
  • 여담
    • 드디어 11일차에 처음으로 2시에 맞춰서 문제를 풀 기회를 갖게 되었다. 하지만 알게 된것은 점수를 얻기가 너무 빡세다는 것.;
    • Part 1은 8분 19초로 264등. Part 1을 거의 그대로 재활용해서 제출할수 있었던 Part 2는 9분 8초로 97등. 그나마 겨우 처음으로 점수(4점!) 를 얻었다는 것에 만족했다.
  • 코드

Day 12: Hot Springs

  • 풀이
    • 결과적으로는 DP이긴 한데, 점화식을 잘 세워서 바텀업으로 하는 구현보다는, 그냥 일단 백트래킹을 구현해놓고 메모이제이션을 추가하는 느낌의 탑다운 구현이 더 접근하기 쉽다. 이런 형태의 백트래킹은 상당히 전형적인 유형이기도 하고, 가지치기도 이쪽이 좀더 직관적이고..
    • 하지만 처음에는 내가 백트래킹 구현을 많이 싫어하는 관례로 어떻게든 바텀업으로 구현하려고 시도했다. dp테이블을 튜플을 키로 갖는 딕셔너리로 만들고 이리저리 하려 하다가, 자꾸 구현상 걸리적거리는 부분이 생겨서 결국 탑다운으로 다시 만들었다.
    • 길이 n짜리 row를 처리한다면, i번째 글자가 j번째 그룹의 k번째 스프링일때의 가능한 가짓수를 dp 테이블로 갖게 된다. (j, k)의 가능한 가짓수는 O(n)이다. 그래서 총 시간복잡도는 O(n^2).
  • 여담
    • 2시부터 2시반까지 30분정도만 시간을 내는것은 별로 어려운일이 아니라고 생각했는데, 생각보다 일이 많다. 오늘도 2시에 맞춰서 코딩 시작은 실패하고 천천히 시작.
    • 어차피 늦은 김에 여유있게 코드를 짜긴 했다. 1시간 40분이 지나서 제출했더니, Part 1은 6230등, Part 2는 1994등의 등수를 받았다.
    • 근데, 여유있게 코드를 짠것은 사실상 핑계고, 2시에 딱 맞춰 시작해서 최대한 빠르게 코드를 작성하려고 했더라도 점수를 딸수 있었을지는 자신이 없다. 100등 커트라인이 22분인데, 내가 그것보다 빨리 짤수 있었을까..
    • Part 1의 1등 기록은 약 2분, Part 2는 약 6분이다. 미친듯
  • 코드

Day 13: Point of Incidence

  • 풀이
    • 거울 찾기 로직을 세워서 row 기준으로 한번 돌리고, col 기준으로 한번 돌리면 된다. row기준으로 설명하자.
    • 거울 찾기 로직은 결국 어떤 시퀀스에서 대칭점을 찾는 것인데, 문자열 매칭 알고리즘을 잘 돌리면 더 효율적인 방법도 있을 것 같긴 하지만, 그냥 무식하게 비교해도 O(n^2)의 비교면 돌아간다.
    • 비교해야하는 값들이 열에 해당하는 문자열이므로, 그냥 무식하게 비교하면 O(m)이 걸린다, 이렇게 O(n^2)번 비교하면 O(m*n^2)이 되므로 이건 좀 너무 무식한것 같고.. 대신 각 열 또는 행을 해시해서 O(1)에 비교가 가능한 값으로 바꿔주고 처리했다. 그러면 전처리에 O(n*m), 비교에 O(n^2 + m^2)이 걸린다.
    • Part2는 비교할때 그냥 값이 같은지 다른지만 체크하는 것이 아니라, 다른부분이 몇개인지를 체크해야 한다. 이렇게 되면 Part1에서 구현한 방식인 문자열을 해싱해서 해싱된 값이 같냐 다르냐로 비교하는 것은 불가능하다. 그렇다고 O(m)으로 무식하게 비교하는 것은 맘에 안들고, 대신 문자열에 문자가 2종류밖에 없으니까 bitset으로 변환한 뒤에 xor을 구해서 1의 갯수를 세는 방식으로 다른부분의 갯수를 빠르게 셀수 있었다. O(n^2*m/w + m^2*n/w) 이 될듯.
  • 여담
    • 오늘도 4시쯤에 천천히 코딩 시작ㅜㅜ. 2시간 30분이 지나서 제출했더니, Part 1은 8218등, Part 2는 6167등의 등수를 받았다.
  • 코드

Day 14: Parabolic Reflector Dish

  • 풀이
    • 귀찮은 구현문제.. 풀이는 그냥 시키는 대로 시뮬레이션 시키면 된다.
    • Part2에서는 1000000000 사이클을 돌려야 한다는 조건이 있지만, 어차피 얼마 이상의 사이클을 돌리고 나면, 더이상 사이클을 추가해도 변화가 없어지는 시점이 온다. 그래서 훨씬 적은 횟수의 사이클만 돌려서 답을 구하거나, 이 적은 횟수를 얼마로 잡아야 할지 좀 막막하다면 그냥 이전 사이클의 결과에서 변화가 없어지는 시점까지만 돌려서 구하면 된다.
    • 2d 그리드 형태에서 열이나 행을 효율적으로 추출할수 있는 코드 스니펫을 만들어 두면 좋겠다는 생각을 예전부터 갖고 있으면서 안 만들었었는데, 그게 있었으면 지금 딱 사용할수 있었을것 같다. 지금 구현은, 그리드를 1차원 어레이로 저장해서 각 행과 열을 슬라이싱된 리스트와 비슷한 형태로 형태로 추출하도록 구현했는데, 이제라도 이걸 좀 깔끔하게 해서 스니펫으로 정리해둬야겠다.
  • 여담
    • 오늘은 월루시간에 문제를 잡는데에 실패. 늦은김에 아예 천천히 새벽에 풀었다. 10시간 정도 지나서 제출하니 15000등~25000등 사이에 걸리는구나.
  • 코드

Day 15: Point of Incidence

  • 풀이
    • 그냥 시키는 대로 구현하면 되는 문제이다. Part 1은 문자열 해시 함수 (그것도 가장 전형적인) 을 구현하면 끝이고, Part 2는 값들을 이것저것 시키는 대로 저장해야 하지만, 딱히 고급 자료구조를 쓸 필요는 없다.
    • Part2에서 박스에서 렌즈를 지우는 작업을 처리할때, 박스를 그냥 배열로 구현하면 O(n)의 시간이 걸린다. 이것을 효율적으로 처리하는 방법은 물론 존재한다. 바로 떠오르는 것만 해도, 박스를 딕셔너리로 처리하고, 렌즈를 추가할때마다 타임스탬프를 렌즈에 기록해주면, 추가/삭제 모두 O(1)에 되고, 맨 마지막에 남은 렌즈들을 타임스탬프 순서대로 정렬해주기만 하면 된다. 이러면 O(n^2)을 O(nlogn)으로 줄일수 있다.
    • 하지만, 데이터양이 크지 않기 때문에 굳이 이런 최적화를 해줄 필요는 없다. 그냥 배열로 '빨리' 구현하는것이 중요한 문제였다.
  • 여담
    • 모처럼 2시에 맞춰서 코딩을 시작했지만.. 문제가 너무 간단한 문제였다. 코딩 시간보다, 문제를 읽고 해석하는데 걸리는 시간이 큰 비율을 차지하는 문제.. 게다가 문제가 너무 간단하다보니 내가 이해한게 맞나 싶어서 여러번 더 문제를 꼼꼼히 읽어야 했는데 여기서 시간을 더 크게 소모했다 ㅜㅜ
    • Part 1은 3분 32초에 제출해서, 528등. Part2는 15분 43초에 제출해서 450등.. 포인트를 얻는데에는 실패 ㅜ
  • 코드

Day 19: Aplenty

  • 풀이
    • 으어.. 귀찮은 구현 문제.. 날이 지날수록 알고리즘 난이도가 올라가는게 아니라 구현 귀찮음 정도가 올라가고 있다. 원래 이런컨셉인건지 몰랐는데 매우 귀찮네..
    • Part1은 파싱에 초점을 둔 구현이다. 워크플로우별로 룰들을 잘 파싱해서 딕셔너리에 저장해두고, 부품이 들어오면 대응되는 룰을 찾아서 다음 워크플로우로 이동해주면 된다. python에서 eval함수를 쓰는것을 꺼리는 편이지만 이번에는 eval이 너무 딱이라서 사용했다. 부품 하나당 최악의 경우 모든 워크플로우를 한번씩 돌아야 하므로 시간복잡도는 O(n*m) 이다 (n=워크플로우 갯수, m=부품의 갯수)
    • Part2는 백트래킹이 필요하다. 어떤 워크플로우에 구간이 들어오면, 각 조건에 대응되는 구간들로 나눠서 다시 재귀적으로 돌려야 한다. 구현이 귀찮을 뿐이지, 가지치기 같은거는 전혀 신경쓰지 않아도 시간은 널널하다. 모든 워크플로우가 룰을 4개씩 꽉 채워서 갖고 있다면 최대 O(4^n)이고 테스트셋의 n은 570정도라서 당연히 문제가 생기겠지만, 그래프가 단순해서 매우 빨리 돌아간다 (혹시 그래프가 트리 형태라는 말이 있었나?)
  • 여담
    • 조금만 부지런했으면 2시에 시작할수 있었을텐데.. 2시 40분쯤 시작해서 Part1은 58분 제출 3881등, Part2는 1시간19분 제출 1457등.
    • 그나저나 어제와 그제 문제를 못풀었다. 시간날때 업솔빙을 해서 메꾸려고 생각하고 있긴 했는데.. 점점 문제가 구현 위주의 재미없는 문제로 나오다 보니 풀 의욕을 점점 잃어간다…
  • 코드

Day 20: Pulse Propagation

  • 풀이
    • 으어.. Part1은 귀찮은 구현 문제. 일단 문제 읽는데만도 10분이 걸렸다. 그러고도 부품들 기능을 잘못 이해해서 중간중간 다시 문제 확인하고 디버깅하고를 반복.. 구현 자체는 데이터를 열심히 파싱해서 그래프를 만들어주고, BFS 형태로 pulse를 뿌려주는 과정을 시뮬레이션 하면 된다.
    • Part2는… 정해가 뭔지 잘 모르겠다;; 일단 Part1에서 만든 시뮬레이션 로직을 계속 반복하다가 목표 모듈에 low가 들어오면 종료하는 식으로 돌렸는데, 버튼을 수백만번 눌러도 조건이 만족되지 않는다. 짧은 시간내에 돌아갈 기미가 안보여서 방법을 선회
    • 선회..하긴 했는데 어떻게 해야할지 감이 잘 안온다. 일단 각 모듈에 들어오는 펄스들은 주기성을 가질것이므로, 최종 모듈에 직접적으로 영향을 주는 모듈들을 데이터에서 눈으로 찾았다. 네개의 모듈이 컨정션 모듈을 거쳐서 최종 모듈로 가는것을 확인했고, 그 네개의 모듈에서 나오는 펄스들이 어떤 주기로 변화하는지를 시뮬레이터를 돌려서 찾았다. 돌려보니 각각 모듈이 서로 다른 특정 주기에 한번씩 LOW 펄스를 보내는 것을 확인. 그 특정 주기들의 최소공배수를 따로 계산해서 제출했더니 정답이 되긴 했다.
    • 어찌어찌 풀긴 풀었지만, 뎁스가 하나만 더 깊어졌어도 이런 수동 노가다로는 못푸는거였을텐데.? 제대로 하려면 모든 모듈에 대해서 low pulse의 주기를 계산해야 할것 같다. 위상정렬 순서대로 선행 모듈들의 주기들로부터 현재 모듈의 주기를 구하는 방식의 DP를 구현하는게 정해일거 같은데.. 아아 딱 봐도 구현하기 너무 귀찮게 생겼다..
  • 여담
    • 2시에 딱 맞춰서 시작했다. 그러나 너무 난이도가 높아서 Part2까지 푸는데에 무려 1시간이 걸렸다. 별은 당연히 못받았고.
      • Part1은 45분 제출에 766등, Part2는 58분 제출에 288등.
    • 아 그나저나 진짜 점점 더 재미없어진다. 난이도가 점점 올라가는데, 올라가는 방향이 구현쪽 난이도이다보니.. 그래도 며칠 안남았는데 며칠만 더 참고 끝까지 해볼까 ㅜㅜ

토론

댓글을 입력하세요:
R Y C G G
 
ps/advent_of_code/2023.txt · 마지막으로 수정됨: 2023/12/20 06:16 저자 teferi