출처
그리디 알고리즘
- 현재 상황에서 지금 당장 좋은 것만 고르는 방법
- 문제를 풀기 위한 최소한의 아이디어를 떠올릴 수 있는 능력을 요구
- 정당성 분석
- 가장 좋아보이는 것만 반복적으로 선택해도 최적의 해를 구할 수 있는지 검토
- 최적의 해를 보장할 수 없을 때가 많음
- 그리디 문제로 주어졌다면(탐욕법으로 풀 수 있는 문제라면), 어떠한 접근으로 그리디로 풀 수 있을지 접근방식을 추론할 수 있어야한다.
문제: 거스름 돈
- 500, 100, 50, 10원 짜리 동전을 무한하게 가지고 있음
- 손님에게 거슬러 주어야 할 돈이 N원일 때 거슬러 주어야 할 동전의 최소 개수 구하기
풀이
- 가장 큰 화폐 단위부터 돈을 거슬러 주기
- 500원에서 부터 100, 50, 10 순서대로 거슬러 줄 수 있을만큼 거슬러 주기
- N = 1,260원
- 500 * 2 = 1000
- 100 * 2 = 200
- 50 * 1 = 50
- 10 * 1 = 10
정당성 분석
- 큰 단위가 항상 작은 단위의 배수이므로 작은 단위의 동전들을 종합해 다른 해가 나올 수 없기 때문
- N = 800, 화폐 단위가 500, 400, 100이라면 위와 같은 방법으로는 최적의 해가 나올 수 없다.
코드
n = 1260
cnt = 0
arr = [500, 100, 50, 10]
for coin in arr:
cnt += n // coin
n %= coin
print(cnt)
시간 복잡도 분석
- K = 화폐의 종류의 수
- 시간 복잡도 = O(K)
문제: 1이 될 때까지
- N이 1이 될 때까지 다음의 두 과정을 반복적으로 선택하여 수행
- N에서 1을 뺀다
- N을 K로 나눈다 (나누어 떨어질 때만 선택 가능)
- N과 K가 주어질 때 N이 1이 될 때까지 수행해야되는 과정의 최소 횟수 구하기
- 1 <= N <= 100,000
- 2 <= K <= 100,000
풀이
- K로 나눌 수 있을 때 먼저 나눠주기
- 나누지 못할 때는 나눌 수 있을 때까지 1 빼주기
- 25 3
- 25 - 1 = 24
- 24 / 3 = 8
- 8 - 1 = 7
- 7 - 1 = 6
- 6 / 3 = 2
- 2 - 1 = 1
정당성 분석
- N이 아무리 큰 수여도, K가 2 이상이기만 하면 K로 나누는 것이 1을 빼는 것보다 항상 빠르게 N을 줄일 수 있다.
- 또한 N은 항상 1에 도달하게 된다. (최적의 해 성립)
코드
n, k = map(int, input().split())
cnt = 0
while True:
target = (n // k) * k # k로 나눌 수 있고 n보다 작고 n에서 가장 가까운 수
cnt += (n-target) # n이랑 차이 나는 만큼 빼주어야 함
n = target
# n을 k로 더이상 나눌 수 없을 때 반복문 탈출
if n < k:
break
# k로 나누기
cnt += 1
n //= k
# 남은 수에 대해 1이 될 때까지 빼기
cnt += (n-1)
print(cnt)
문제: 곱하기 혹은 더하기
- 숫자(0부터 9)로만 이루어진 문자열 S
- 숫자 사이에 ‘×’혹은 ‘+’ 연산자를 넣어 만들어질 수 있는 가장 큰 수 구하기
- 모든 연산은 왼쪽에서부터 순서대로 이루어진다고 가정
풀이
- 두 수 중 하나라도 0이거나 1이라면 + 아니라면 ×
- 02984
- ((((0 + 2) × 9) × 8) × 4) = 576
정당성 분석
- 두 수 모두 2 이상이라면 곱하기가 훨씬 더 수를 크게 만든다.
코드
s = input()
result = int(s[0]) # 첫 번째 숫자로 시작
for i in range(1, len(s)):
# 두 수 중에 하나라도 0이거나 1이라면 더하기 수행
num = int(s[i])
if num <= 1 or result <= 1:
result += num
else:
result *= num
print(result)
문제: 모험가 길드
- 모험가 N명
- 공포도가 X인 모험가는 반드시 X명 이상으로 구성한 모험가 그룹에 참여
- 여행을 떠날 수 있는 그룹 수의 최댓값 구하기
- 모든 모험가를 그룹에 넣을 필요는 없다.
풀이
- 오름차 순으로 정렬
- 공포도가 낮은 순으로 확인하며 현재 그룹에 포함된 모험가 수가 현재 확인하고 있는 공포도보다 크거나 같다면 그룹으로 설정
- 2 3 1 2 2
- 1 2 2 2 3 (오름차순 정렬)
- (1) 2 2 2 3
- (1) (2 2) 2 3
정당성 분석
- 공포도가 오름차순으로 정렬되어있어 항상 최소한의 모험가 수만 포함하는 그룹을 만들게 된다.
코드
n = int(input())
data = list(map(int, input().split()))
data.sort()
result = 0 # 총 그룹의 수
cnt = 0 # 현재 그룹에 포함된 모험가 수
for i in data:
cnt += 1 # 현재 그룹에 현재 모험가 추가
if cnt >= i: # 현재 그룹에 포함된 모험가 수가 현재 모험가의 공포도 이상이라면 그룹 결성
result += 1 # 그룹 수 증가
cnt = 0 # 현재 그룹 모험가 수 초기화
print(result)
Leave a comment