Search
Duplicate

주사위 고르기

Level
3
문제 진행 상태
코드 완료
해설 완료
알고리즘 & 자료구조
이분탐색
정답률 (%)
15
태그
2024 KAKAO WINTER INTERNSHIP

문제 링크

풀이과정

•
A와 B가 뽑은 주사위의 조합이 정해졌다고 가정합시다.
•
가장 쉬운 방법은, A의 주사위 눈의 합과 B의 주사위 눈의 합을 구해서 비교하는 것입니다.
◦
예를 들어, 문제 예시에 따라 A는 #1, #3번 주사위를, B는 #2, #4번 주사위를 뽑았다고 합시다.
◦
이때, A는 1, 3번 주사위를 통해 눈 [1, 4]를 B는 2, 4번 주사위를 통해 눈 [3, 5]를 뽑았다고 가정하면, A는 총점 5 / B는 총점 8을 획득했으므로 B가 승리합니다.
◦
이러한 과정을 주사위를 선택하는 모든 경우에 대하여, 그리고 선택한 주사위에서 나올 수 있는 모든 눈의 조합에 대하여 계산할 수 있습니다.
•
해당 방법대로 시간 복잡도를 계산해보겠습니다.
◦
주사위 최대 갯수는 10개입니다.
◦
따라서, 주사위를 선택하는 모든 경우의 수는 10C5(최댓값)입니다.
◦
이제, 선택된 주사위의 조합이 정해졌다고 가정합시다.
◦
A도 주사위 5개, B도 주사위 5개를 가지고 있으므로, 가능한 주사위 눈 조합의 경우의 수는 (6^5 * 6^5)입니다.
◦
그러므로, 최악의 경우 시간 복잡도는 10C5 * (6^5 * 6^5) 로서, 시간 초과 가능성이 존재합니다. (C는 Combination, ^는 거듭제곱을 의미합니다.)
•
따라서, 시간 복잡도 이슈를 피하기 위해서 다른 방법을 생각해야 합니다.
•
우리는 A와 B의 주사위 눈을 함께(연쇄적으로) 구하여 주사위 눈의 합을 구하고, 비교하는 과정을 수행했습니다.
•
이때, A가 낼 수 있는 주사위 눈의 조합과 B가 낼 수 있는 주사위 눈의 조합을 따로 구하면 어떨까요?
◦
즉, 10C5 * (6^5 * 6^5)를 10C5 * 6^5의 시간 복잡도로 낮추는 것입니다.
•
그 다음, 다음 과정을 수행합니다.
1.
A의 주사위를 통해 가능한 모든 주사위 눈의 합을 구하고, B도 동일한 과정을 반복합니다. (각각 리스트 자료 구조로 관리합니다)
2.
A와 B의 리스트(원소: 주사위 눈의 합)를 오름차순으로 정렬합니다.
3.
A의 리스트를 순회하면서, 현재 주사위 눈의 합을 통해, B를 이길 수 있는 경우의 수를 구합니다.
4.
3에서 구한 경우의 수를 합산합니다.
•
이를 위해, 조합과 이분 탐색으로 문제를 접근해보겠습니다.

전체 코드

from itertools import combinations, product def solution(dice): ''' A가 이기는 것을 기준으로 함. ''' answer = [] dice_indices = [i for i in range(len(dice))] # 주사위 인덱스 pickup_num = int(len(dice) / 2) # 뽑을 주사위 갯수 win_max = 0 for a_dices in combinations(dice_indices, pickup_num): a_dices = set(a_dices) # A가 뽑은 주사위 집합 (ex: (1, 2)) b_dices = set(dice_indices) - a_dices # B가 뽑은 주사위 집합 (ex: (3, 4)) ### A와 B 따로따로 계산 수행 ### # A, B 각각 나올 수 있는 주사위 눈의 조합 itemsA = [dice[a_idx] for a_idx in a_dices] # ex 2 itemsB = [dice[b_idx] for b_idx in b_dices] # ex 2 product_listA = list(product(*itemsA)) # 6^2 product_listB = list(product(*itemsB)) # 6^2 winA = 0 # A가 승리할 수 있는 방법의 수 # A, B 각각 주사위 눈의 조합으로 얻을 수 있는 숫자 합 sum_tempA, sum_tempB = [], [] # A에 대하여, 가능한 주사위 눈의 숫자 합을 저장 for a in product_listA: sum_tempA.append(sum(list(a))) # B에 대하여, 가능한 주사위 눈의 숫자 합을 저장 for b in product_listB: sum_tempB.append(sum(list(b))) # A, B에 대한 주사위 숫자 합 정렬 sum_tempA = sorted(sum_tempA) sum_tempB = sorted(sum_tempB) for target in sum_tempA: # target이 B의 주사위 눈 합의 최솟값보다 작은 경우 if target <= sum_tempB[0]: continue # target이 B의 주사위 눈 합의 최댓값보다 큰 경우 if target > sum_tempB[-1]: winA += len(sum_tempB) continue ### 이분 탐색 수행 ### left, right = 0, len(sum_tempB) - 1 find = 0 while left <= right: mid = (left + right) // 2 if target > sum_tempB[mid]: left = mid + 1 elif target < sum_tempB[mid]: right = mid - 1 # target을 B에서 찾아도, 동일한 값이 이전 인덱스에도 존재할 수 있습니다. # 우리는 A가 이기는 경우(점수가 높은 경우)만을 고려해야 합니다. # 따라서, right를 줄여서 계속 이분 탐색을 수행해야 함을 잊지 마세요! else: find = mid right = mid - 1 # A가 이길 수 있는 방법의 수 갱신 if find != 0: winA += find else: winA += (right + 1) # 갱신된 방법의 수가 최댓값인 경우 if winA > win_max: answer = [idx+1 for idx in a_dices] win_max = winA return sorted(answer)
Python
복사

유사 문제

이분 탐색은 다음과 같은 조건 하에서 특정 값을 찾아내기 위하여 생각해볼 수 있는 해결책입니다.
•
순차 탐색에 필요한 시간이 과도하게 많이 드는 경우
•
탐색 대상이 정렬되어 있는 경우
유사 문제는 아래와 같습니다.