그리디 알고리즘은 일반적으로 문제를 풀기 위한 최소한의 아이디어를 떠올릴 수 있는 능력을 요구한다.
그리디 알고리즘은 그 정당성 분석이 중요하다. 단순히 가장 좋아 보이는 것을 반복적으로 선택해도 최적의 해를 구할 수 있는지를 검토해야 한다. 왜냐하면 일반적 상황에서 그리디 알고리즘은 최적의 해를 보장할 수 없을 때가 많기 때문이다.
그러나 코딩테스트 대부분은 탐욕법으로 얻은 해가 최적의 해가 되는 상황에서 이를 추론할 수 있어야 풀리도록 출제된다.
예제
거스름돈 문제
거스름돈을 가장 적은 동전의 개수로 거슬러주는 문제가 대표적인 그리디 알고리즘 문제이다.
이 문제가 그리디 알고리즘으로 최적의 해를 보장하는 정당성이 무엇일까?
500원, 100원, 50원, 10원 등 가지고 있는 동전들은 큰 단위가 항상 작은 단위의 배수이므로 작은 단위의 동전들을 종합해서 더 적은 동전의 개수를 가진 해가 나올 수 없기 때문이다.
예를 들어 화폐단위가 500원, 400원, 100원 이라면 그리디 알고리즘으로 최적의 해를 보장할 수 없다. 큰 단위가 작은 단위의 배수가 아니기 때문이다.
n=1260count=0# 큰 단위의 화폐부터 차례대로 확인하기
coin_types=[500,100,50,10]forcoinincoin_types:count+=n//coin# 해당 화폐로 거슬러 줄 수 있는 동전의 개수 세기
n%=coinprint(count)
이처럼 그리디 알고리즘 문제에서는 문제 풀이를 위한 최소한의 아이디어를 떠올리고 이것이 정당한지 검토할 수 있어야 한다.
위 거스름돈 예제에서 그리디 알고리즘이 최적의 해를 보장할 수 없다면, DP 를 사용할 수 있다.
Fractional Knapsack Problem
물건을 쪼갤 수 있는 배낭 문제이다.
가치가 높은 순으로 정렬한 뒤 배낭에 담고, 남은 부분은 물건을 쪼개어 넣어 조합을 찾는다.
대표적인 그리디 알고리즘으로 해결할 수 있는 문제이다.
# 이익, 무게
cargo=[(4,12),(2,1),(10,4),(1,1),(2,2),]deffractional_knapsack(cargo):capacity=15pack=[]# 단가(이익 / 무게) 계산 역순 정렬
forcincargo:pack.append((c[0]/c[1],c[0],c[1]))pack.sort(reverse=True)# 단가 순 그리디계산
total_value:float=0forpinpack:ifcapacity-p[2]>=0:capacity-=p[2]total_value+=p[1]# 가방의 용량을 넘어서는 경우, 가방의 남은 용량만큼만 짐을 넣는다.
else:fraction=capacity/p[2]total_value+=p[1]*fractionbreakreturntotal_valuer=fractional_knapsack(cargo)print(r)
만일 물건을 쪼갤 수 없는 Knapsack problem 은 DP 를 사용해서 해결 가능하다.
주유소 문제
주유 비용을 최소로 하는 문제이다.
현재 도시에서 주유할 수 있는 비용과 다음 도시에서 주유할 수 있는 비용을 비교해서 적은 비용을 유지하면서 거리를 곱한다.
importsysfromcollectionsimportdequen=int(input())dist=deque(list(map(int,sys.stdin.readline().split())))cost=deque(list(map(int,sys.stdin.readline().split())))# 맨 처음 도시에서는 비용에 관계없이 주유해야 한다.
now_cost=cost.popleft()now_dist=dist.popleft()total_cost=now_cost*now_distwhiledist:next_cost=cost.popleft()ifnow_cost>next_cost:now_cost=next_costnow_dist=dist.popleft()total_cost+=now_dist*now_costprint(total_cost)
댓글 남기기