developer-ellen
인간 디버거의 로그 찍기
developer-ellen
전체 방문자
오늘
어제
  • 분류 전체보기 (217)
    • 회고록 (0)
    • 취뽀 및 커리어 여정의 Stack (2)
      • SSAFY 7기 (2)
    • 프로그래밍공부 (24)
      • c++ (0)
      • JAVA (3)
      • Spring (5)
      • design pattern (3)
      • BackDB (1)
      • Servlet&JSP (3)
      • Vue (4)
      • JPA (4)
      • Infra (1)
      • Linux (0)
    • AI (3)
      • papers (3)
      • trend (0)
    • 프로젝트진행 (0)
      • 데이터베이스 (0)
      • 서버개발 (0)
      • 인공지능 (0)
      • 하루정리 (0)
    • 포트폴리오 (0)
    • 알고리즘 (158)
      • 알고리즘문풀 (155)
      • 알고리즘공부 (3)
    • 통계공부 (15)
      • 시계열분석 (15)
      • 회귀분석 (0)
    • CS (14)
      • 컴퓨터네트워크 (4)
      • 운영체제 (8)
      • 데이터베이스 (2)
    • 주저리주저리 (0)
      • 필사 (0)
    • 취업관련정보 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 백준파이썬풀이
  • c++ 빌더 패턴
  • 데이터분석
  • 삼성코테기출
  • 삼성코테파이썬
  • SW역량테스트파이썬
  • 삼성코테파이썬풀이
  • 삼성코테구현문제추천
  • 삼성코테준비
  • 통계분석
  • 시계열
  • 삼성코테자바준비
  • 삼성코테자바풀이
  • 카카오코테
  • 코테파이썬
  • BOJ파이썬풀이
  • Arima
  • 삼성코테기출자바풀이
  • 통계학
  • 카카오코테java풀이
  • 삼성코테파이썬준비
  • 운영체제인터럽트
  • SW역량테스트파이썬풀이
  • c++디자인패턴
  • AR모형
  • ARIMA모형
  • 삼성코테구현풀이
  • MA모형
  • 삼성코테자바꿀팁
  • 시계열분석

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
developer-ellen

인간 디버거의 로그 찍기

알고리즘/알고리즘공부

[이코테] 그리디 알고리즘

2021. 3. 11. 11:24

그리디 알고리즘 (탐욕법)

  • 어떠한 문제가 있을 때 단순 무식하게, 탐욕적으로 문제를 푸는 알고리즘
  • 현재 상황에서 지금 당장 좋은 것만 고르는 방법
  • 기준에 따라 좋은 것을 선택하는 알고리즘으로 문제에서 '가장 큰 순서대로', '가장 작은 순서대로'와 같은 기준을 제시함

 

 

[ 예제 3-1 ] 거스름돈

당신은 음식점의 계산을 도와주는 점원이다. 카운터에는 거스름돈으로 사용할 500원, 100원, 50원, 10원짜리 동전이 무한히
존재한다고 가정한다. 손님에게 거슬러 줘야 할 돈이 N원 일 때 거슬러 줘야할 동전의 최소 개수를 구하라. 
단, 거슬러 줘야 할 돈 N은 항상 10배수이다.

문제 접근

  1. '가장 큰 화폐 단위부터' 돈을 거슬러 주는 것이 아이디어의 핵심
  2. 예를 들어 N원을 거슬러 준다고 할 때, 500원으로 거슬러 줄 수 있을 만큼으로 거슬러 준 후 그 다음 100원, 50원, 10원짜리 동전을 차례대로 거슬러 줄 수 있을 만큼 거슬러 준다면 최소 동전의 개수만 사용 가능
  3. 큰 화폐단위가 항상 작은 화페단위의 배수 형태인 경우라면 최적의 해를 보장할 수 있음
#예제 3-1 거스름돈 파이썬 풀이법
n = 1260
count = 0

#큰 단위의 화폐부터 차례대로 확인
coin_type = [500, 100, 50, 10]

for coin in coin_type:
	count += n // coin # 해당 화폐로 거슬러 줄 수 있을 동전의 개수 세기
    n %= coin 
    
print(count)

 

시간복잡도

: 화폐의 종류만큼 반복 수행 해야하기 때문에 화폐의 종류가 K개라고 할 때 소스 코드의 시간 복잡도는 O(K)

: 즉 동전의 총 종류에만 영항을 받고, 거슬러 줘야하는 금액의 크기와는 무관함

 

 

그리디 알고리즘의 정당성

  • 그리디 알고리즘으로 문제의 해법을 찾았을 때는 그 해법이 정당한지 검토해야 함
  • 거스름돈 문제가 그리디 알고리즘으로 해결할 수 있는 이유는 가지고 있는 동전 중에서 큰 단위가 항상 작은 단위의 배수이므로 작은 단위의 동전들을 종합해 다른 해가 나올 수 없기 때문
  • 800원을 거슬러 줘야 할 때  화폐 단위가 500원, 400원, 100원인 경우를 생각하면 그리디 알고리즘으로 4개의 동전(500원 + 100원 + 100원 + 100원)을 거슬러 줘야 한다고 나오는데 최적의 해는 2개의 동전 (400원 + 400원)을 거슬러 주는 것임
  • 큰 단위가 작은 단위의 배수 형태이므로, '가장 큰 단위의 화폐부터 가장 작은 단우의 화폐까지 차례대로 확인하여 거슬러 주는 작업만을 수행하면 된다'는 아이디어는 정당함
  • 대부분의 그리디 알고리즘문제에서는 이처럼 문제 풀이를 위한 최소한의 아이디어를 떠올리고 이것이 정당한지 검출할 수 있어야 답을 도출할 수 있음
  • 어떤 코딩 테스트 문제를 만났을 때, 바로 문제 유형을 파악하기 어렵다면 그리디 알고리즘을 의심하고 문제를 해결할 수 있는 탐욕적인 해결법이 존재하는 지 고민해보자. 만약 오랜 시간을 고민해도 그리디 알고리즘으로 해결 방법을 찾을 수 없다면 그 때는 다른 알고리즘으로 문제를 해결할 수 있는지 재차 고민해보는 것도 한 방법임

 

[ 예제 3-2 ] 큰 수의 법칙

입력 조건

  • 첫째 줄에 N(2 <= N <= 1000), M(1 <= M <= 10000), K(1<= K <= 10000)의 자연수가 주어지며, 각 자연수는 공백으로 구분한다.
  • 둘째 줄에 N개의 자연수가 주어진다. 각 자연수는 공백으로 구분한다. 단 각각의 자연수는 1이상 10,000 이하의 수로 주어진다.
  • 입력으로 주어지는 K는 항상 M보다 작거나 같다.

출력 조건

  • 첫째 줄에 동빈이의 큰 수의 법칙에 따라 더해진 답을 출력한다.
입력 예시

5 8 3
2 4 5 4 6
출력 예시

46

 

 

문제 접근 1 ( 단순하게 풀기)

: 이 문제를 해결하기 위해서 일단 입력 값 중에서 가장 큰 수와 두 번째로 큰 수만 저장하면 된다 연속으로 더할 수 있는 횟수는 최대 K번 이므로 '가장 큰 수를 K번 더하고 두 번째로 큰 수를 한 번 더하는 연산'을 반복하면 됨

 

n, m, k = map(int, input().split())
data = list(map(int, input().split()))

data.sort() # 입력받은 수 정렬
first = data[n-1] # 가장 큰 수
second = data[n-2] # 가장 작은 수

result = 0

while True:
	for i in range(k):	# 가장 큰 수 K번 먼저 더하기
    	if m == 0:
        	break
        result += first
        m -= 1	# 더할 때마다 1씩 빼기
    if m == 0:
    	break
    result += second
    m -= 1	# 더할 때마다 1씩 빼기


print(result)

: 이 문제는 M이 10,000 이하이므로 이 방식으로 문제 해결 가능하지만, M의 크기가 100억 이상처럼 커진다면 시간 초과 판정을 받을 수 있음 따라서 간단한 수학적 아이디어를 활용한다면 더 효율적으로 문제 해결이 가능함

 

 

 

문제 접근 2 ( 더 효율적인 방법)

2 4 5 4 6

이 때 가장 큰 수는 6이고 두 번째로 큰 수는 5이다.

6 6 6 5

예를 들어,N = 5, M = 8 이고 K =3일 때 합을 구성할 수 있는 수가 2, 3, 5, 4, 6이라면 위와 같이 더했을 때가 합을 최대로 할 수 있다.

즉, ( 6 + 6 + 6 + 5 )가 2번 반복해서 정답이 46이 된다. 

 

 

  • 이 문제의 처음 접근 방식은 반복되는 수열에 대해서 파악해야함
  • 가장 큰 수와 두 번째로 큰 수가 더해질 때는 특정한 수열 형태로 일정하게 반복해서 더해지는 특징이 있음
  • 반복되는 수열의 길이는 ( K + 1 )
  • 수열이 반복되는 횟수는 M / ( K + 1 ) 이며, 여기에 K를 곱하면 가장 큰 수가 등장하는 횟수
  • M이 ( K + 1 ) 로 나누어 떨어지지 않는 경우도 고려할 때 M을 ( K + 1 ) 로 나눈 나머지만큼 가장 큰 수가 추가로 더해짐
  • 즉 '가장 큰 수가 더해지는 횟수'는 int( M / ( K + 1 ) ) * K + M % ( K + 1 )

 

n, m, k = map(int, input().split())
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)

 

 

 

 

Ellen's thinking

이 책의 저자가 소개하듯이, 알고리즘 문제를 처음 마주할 때 그리디 알고리즘인지 의심하고 고민해야겠다. 그러나 시간이 오래 지날 수록 해결법이 떠오르지 않는다면 다이나믹 프로그래밍이나 그래프 알고리즘을 떠올려서 문제를 풀어야한다. 또한 그리디 알고리즘으로 풀 때 단순히 해결법만 생각해서 소스코드로 구현한다면 그 방식이 틀릴 수 있다. 따라서 자신이 생각한 해결법이 최적의 해를 보장하는지 근거까지 도출할 수 있어야한다. 그리디 알고리즘은 여러 문제를 풀어보며 익혀야 할 것 같다.

 

'알고리즘 > 알고리즘공부' 카테고리의 다른 글

[이코테] 스택, 큐, 재귀  (0) 2021.03.24
[이코테] 구현  (0) 2021.03.11
    '알고리즘/알고리즘공부' 카테고리의 다른 글
    • [이코테] 스택, 큐, 재귀
    • [이코테] 구현
    developer-ellen
    developer-ellen

    티스토리툴바