-
PART02 - CHAPTER 03. 그리디algorithm/이것이 취업을 위한 코딩테스트다 2023. 3. 24. 12:46
1. 당장 좋은 것만 선택하는 그리디
- 그리디 알고리즘은 어떠한 문제가 있을 때 단순 무식하게, 탐욕적으로 문제를 푸는 알고리즘이다.
ㆍ현재 상황에서 지금 당장 좋은 것만 고르는 방법
- 그리디 알고리즘을 이용하면 매 순간 가장 좋아 보이는 것을 선택하며, 현재의 선택이 나중에 미칠 영향에 대해서는 고려하지 않는다.
- 정렬, 최단 경로 등의 알고리즘 유형은 이미 알고리즘의 사용 방법을 정확히 알고 있어야만 해결 가능한 경우가 많다.
- 예를 들어 여러 개의 데이터를 빠르게 정렬해야 하는 문제는 정렬 라이브러리의 사용 방법을 알고 있어야한다.
또 다른 예시로 최단 경로를 빠르게 찾아야 하는 문제는 플로이드 워셜 혹은 다익스트라 알고리즘과 같은 특정 알고리즘을 미리 알고 있어야한다.
- 그리디 알고리즘은 사전 지식 없이도 풀 수 있는 문제이지만, 많은 유형을 접해보고 문제를 풀어보며 훈련을 해야한다.
- 그리디 알고리즘은 기준에 따라 좋은 것을 선택하는 알고리즘이므로 문제에서 '가장 큰 순서대로', '가장 작은 순서대로'와 같은 기준을 알게 모르게 제시해준다.
- 대체로 이 기준은 정렬 알고리즘을 사용했을 때 만족시킬 수 있으므로 그리디 알고리즘 문제는 자주 정렬 알고리즘과 짝을 이뤄 출제된다.
예제 3-1 거스르돈
n = 1260 count = 0 # 큰 단위의 화폐부터 차례대로 확인 coin_types = [500, 100, 50, 10] for coin in coin_types: count += n//coin # 해당 화폐로 거슬러 줄 수 있는 동전의 개수 세기 n %= coin print(count)
그리디 알고리즘의 정당성
- 대부분의 그리디 알고리즘 문제에서는 이처럼 문제 풀이를 위한 최소한의 아이디어를 떠올리고 이것이 정당한지 검토할 수 있어야 답을 도출할 수 있다.
- 어떤 코딩 테스트 문제를 만났을 때, 바로 문제 유형을 파악하기 어렵다면 그리디 알고리즘을 의심하고, 문제를 해결할 수 있는 탐욕적인 해결법이 존재하는지 고민해보자.
- 만약 오랜 시간을 고민해도 그리디 알고리즘으로 해결 방법을 찾을 수 없다면, 그때는 이후의 장에서 다루게 될 다이나믹 프로그래밍이나 그래프 알고리즘 등으로 문제를 해결할 수 있는지를 재차 고민해보는 것도 한 방법이다.
* 기업의 코딩 테스트를 안정적으로 통과하려면 다음에 소개되는 문제 각각에 대해 문제별 아이디어를 떠올리고 코드로 작성하기까지 시간이 30분 이내로 소요되어야 한다.
2. [실전문제] 큰 수의 법칙
- 이문제를 풀려면 가장 먼저 반복되는 수열에 대해서 파악해야 한다.
# N, M, K를 공백으로 구분하여 입력받기 n, m, k = map(int, input().split()) # N개의 수를 공백으로 구분하여 입력받기 data = list(map(int, input().split())) data.sort() # 입력받은 수 정렬 first = data[n-1] # 가장 큰 수 second = data[n-2] # 두 번째로 큰 수 # 가장 큰 수가 더해지는 횟수 계산 count = int(m / (k+1)) * k count += m % (k+1) result = 0 result += (count) * first # 가장 큰 수 더하기 result += (m - count) * second # 두 번째로 큰 수 더하기 print(result) # 최종 답안 출력
- m : 더해지는 횟수 , k : 반복가능한 횟수 , m/(k+1) : 한 사이클의 횟수 , m/(k+1) * k : 한 사이클의 횟수 x 반복횟수
m%(k+1) : 사이클의 남은 횟수
3. [실전문제] 숫자 카드 게임
# N, M을 공백으로 구분하여 입력받기 n, m = map(int, input().split()) result = 0 # 한 줄씩 입력받아 확인 for i in range(n): data = list(map(int, input().split())) # 현재 줄에서 '가장 작은 수' 찾기 min_value = min(data) # '가장 작은 수'들 중에서 가장 큰 수 찾기 result = max(result, min_value) print(result)
# N, M을 공백으로 구분하여 입력받기 n, m = map(int, input().split()) result = 0 # 한 줄씩 입력받아 확인 for i in range(n): data = list(map(int, input().split())) # 현재 줄에서 '가장 작은 수' 찾기 min_value = 10001 for a in data: min_value = min(min_value, a) # '가장 작은 수'들 중에서 가장 큰 수 찾기 result = max(result, min_value) print(result)
4. [실전문제] 1이 될 때까지
- 과정이 아니라 결과에 집중하자!
- '최대한 많이 나누기'
- 단순하게 푸는 답안 예시
n, k = map(int, input().split()) result = 0 # N이 K이상이라면 K로 계속 나누기 while n >= k: # N이 K로 나누어 떨어지지 않는다면 N에서 1씩 빼기 while n % k != 0: n -= 1 result += 1 # K로 나누기 n //= k result += 1 # 마지막으로 남은 수에 대하여 1씩 빼기 while n > 1: n -= 1 result += 1 print(result)
ㆍ문제에서 N의 범위가 10만 이하이므로, 이처럼 일일이 1씩빼도 문제를 해결할 수 있다.
ㆍ하지만 N이 100억 이상의 큰 수가 되는 경우를 가정했을 때에도 빠르게 동작할려면, N이 K의 배수가 되도록 효율적으로 한 번에 빼는 방식으로 소스 코드를 작성할 수 있다.
# N, K를 공백으로 구분하여 입력받기 n, k = map(int, input().split()) result = 0 while True: # (N == K로 나누어떨어지는 수)가 될 때까지 1씩 빼기 target = (n//k) * k result += (n-target) n = target # N이 K보다 작을 때(더 이상 나눌 수 없을 때) 반복문 탈출 if n < k : break # K로 나누기 result += 1 n //= k result += (n-1) print(result)
'algorithm > 이것이 취업을 위한 코딩테스트다' 카테고리의 다른 글
PART02 - CHAPTER 07. 이진 탐색 (0) 2023.03.26 PART02 - CHAPTER 06. 정렬 (0) 2023.03.26 PART02 - CHAPTER 05. DFS/BFS (0) 2023.03.24 PART02 - CHAPTER 04. 구현 (0) 2023.03.24 이것이 취업을 위한 코딩테스트다 - 1. 코딩 테스트 개요 (0) 2022.06.13