알고리즘 트레이닝/Dynamo로 푸는 백준

Dynamo로 풀어보자-백준 2480번(주사위 세개)

hnanmal 2023. 1. 25. 06:11
300x250

입력값의 상황에 따라

입력값이 어떤 컨디션인지에 따라 다른 논리를 적용하여 계산을 해야 하는 상황이 있습니다. 덩치 큰 솔루션이나 라이브러리를 구상할 때는 객체지향형 프로그래밍 패러다임 하에서는 입력된 값을 분별하는 인터페이스를 따로 디자인하는 것이 정돈되고 깔끔한 방법이겠지만, 다이나모에서는 구현할 수 없고, 또한 (반쪽짜리이지만) 함수형 프로그래밍 패러다임과 유사하게 동작하는 다이나모에서는 굳이 구현할 이유도 없습니다.

멀티메소드를 구현해서 문제 풀이하는 방법도 있겠는데, 여기서는 단순히 딕셔너리를 활용하여 멀티메소드를 반쯤 흉내내는 방식의 풀이를 보이겠습니다. (제대로 멀티메소드를 구현하는 풀이는 나중에 따로 포스팅 하겠습니다.)

 

문제 및 입력 조건

2480번: 주사위 세개

 

 

 

다이나모 풀이

전체의 풀이는 아래와 같습니다.

 

 

 

 

입력부

 

먼저 3개의 주사위를 던져서 나온 값을 코드블럭으로 입력 받습니다.

공백으로 분리된 문자열로 입력받게 되고, 원활하게 입력값을 교체하며 테스트하기 위해서 placeholder 노드를 하나 만들어 둡니다.

 

 

입력값 분리 및 동일 주사위 눈 개수 파악

 

이 문제를 푸는 데 가장 핵심이 되는 포인트는, 같은 눈의 갯수가 몇개인가 입니다. 문제의 조건을 다시 한번 볼까요?

 

같은 눈이 3개, 2개, 그리고 모두 다른 눈 일 때의 경우로 나뉩니다.

 

이걸 파악하기 위해 여기서는 먼저 List.GroupByKey 노드를 사용해서 같은 종류가 있으면 서브리스트로 묶어주도록 합니다.

(List.GroupByKey 노드를 활용하는 방법은 눈부시게 많은데, 일단 여기서는 풀이와 같이 list와 keys노드에 동일한 데이터를 연결하는 방법만 익혀두세요)

 

입력된 값이 3 3 6 이라서 3과 3이 하나의 서브리스트로 구성된 걸 볼 수 있습니다. 모두 다른 값이라면 개별 값 하나들이 각각 서브리스트 3개로 구성되겠죠?

 

여기서는 3이 두개 들어있는 서브리스트 1개, 6 하나만 들어있는 서브리스트 또 한 개, 즉 총 2개의 서브리스트를 원소로 하는 리스트가 결과값으로 나옵니다.

 

 

 

같은 눈의 개수 파악

 

아까의 결과값에 List.Count 노드를 연결하면 각 각의 서브리스트에 몇 개의 숫자들이 포함되어 있는 지가 나옵니다. (@L2로 표시된 리스트 레벨 설정을 해야 합니다.) 첫번째 서브리스트에 2개 , 두번째 서브리스트에 1개의 항목이 들어있다고 말하는 군요. 이제 여기에 List.MaximumItem 노드를 연결하면, 가장 값이 큰 항목을 반환해 줍니다. 즉 동일한 눈이 최대 몇개가 있는지를 바로 알려주는 노드가 되는 거죠. 만약 6 6 6 이 입력값으로 들어갔다면, List.MaximumItem의 결과값은 3이 될 겁니다. 이걸 나중에 딕셔너리의 키 값으로 써먹기 위해 맨 위쪽으로 따로 빼두고, 수평으로는 본격적인 판별 알고리즘을 진행합시다.

 

List.IndexOf 노드를 통해서, 동일한 숫자가 들어온 서브리스트가 몇번째인지를 판별을 해야 하는데, List.Count의 결과값은 int형이고 List.MaximumItem의 결과값은 double형이라 서로 비교가 안됩니다. 따라서 속 편하게 둘 다 문자열로 형변환 해준 뒤, List.IndexOf에 연결하여, 첫번째 서브리스트 항목이 동일 숫자가 들어온 항목이라는 것을 찾아냅니다.

 

 

 

모든 규칙을 미리 계산해 두기

 

멀티메소드 방식을 적용해서 푸려면, 입력값의 타입을 규정한 뒤에, 각 타입마다 적용할 함수 목록을 준비해 놓고, 딕셔너리에 입력해서 상황에 맞는 함수만 딱 적용하는 방법으로 해야하는데, 그 방법으로 풀려면, 상금 계산 규칙을 하나의 함수로 합성하는 방법을 알아야 합니다. 이걸 설명하려면 포스팅을 별도로 해야 하기 때문에, 여기서는 야매 방법으로 풀겠습니다.

야매인 방법이 뭐냐, 바로 미리 다 계산해버리기 입니다.

 

문제에서 주어진 계산규칙이 3가지 밖에 안되기 때문에, 각 규칙의 결과를 미리 다 계산해 놓고, 인풋값의 상황에 들어맞는 값만 골라서 출력하는 방식입니다.

 

  1. 10,000원+(같은 눈)×1,000원
  2. 1,000원+(같은 눈)×100원
  3. (그 중 가장 큰 눈)×100원

 

이 세가지 경우를 각각 다 계산해 버립니다.

13000, 1300, 600의 계산 결과값이 나오는 군요.

 

 

 

딕셔너리로 맞는 값 찾기

 

이제 피날레입니다. 위쪽에서는 딕셔너리로 중복 숫자가 등장하는 최대값이 3, 2, 1 인 경우를 딕셔너리 형태로 아까 계산해둔 13000, 1300, 600 과 매칭해둡니다.

 

 

그런 다음 아까 초반 부의 List.MaximumItem에서 서브리스트의 항목 갯수 중 최대값을 찾아놓은 값이 있었죠? 이걸 통해 딕셔너리에 2라는 키 값을 주고 어떤 값이 나오는지 조회하도록 알고리즘을 짜면 모두 끝나게 됩니다.

 

 

정확하게 1300원이라고 상금을 계산해주고 있네요.

 

입력값이 2 2 2 인 경우의 상금은 아래와 같습니다.

 

문제에 나와있는 다른 입력값도 직접 테스트해보세요.

 

 

 

결론

입력값이 여러 가지의 상황을 갖고, 각 상황에 맞는 계산을 적용해야 할 때, Clojure 의 멀티메소드 방식을 적용하는게 가장 군더더기 없이 깔끔하다고 생각합니다. 그런데, 저 방식을 구현하려면 함수 합성부터 배워야 해서(다이나모에서는 노드 합성으로 표현해야 할까요?) 이번 포스팅에서는 유사하게 흉내를 내는 방식으로 구현을 했습니다. 하지만 해결해야 하는 문제의 덩치가 커지지 않는 상황이라면, 이 알고리즘으로도 얼마든지 문제를 해결할 수 있으니 유용한 방식이라 생각합니다.

다른 포스팅에서 “Dynamo에서의 함수 합성”이라는 주제로 먼저 포스팅을 한 뒤에, 이 문제의 다른 풀이를 마저 소개하도록 하겠습니다.

반응형