-
[크래프톤 정글] TIL 18일차-그리디크래프톤정글 2024. 9. 19. 20:29
그리디
<aside> 💡
현재 상황에서 지금 당장 좋은 것만 고르는 방법
</aside>
- 최단 경로, 다익스트라 알고리즘은 암기가 필요한 부분이다.
- 기준에 따라 좋은 것을 선택하는 알고리즘이므로 문제에서 ‘가장 큰 순서대로’, ‘가장 작은 순서대로’ 같은 기준을 알게 모르게 제시해준다.
예제 3-1 거스름돈
# 거슬러 줘야 할 돈 N원 N = int(input()) # 거스름돈 종류의 타입 coin_types = [500, 100, 50, 10] result = 0 for coin in coin_types: result += N // coin N %= coin print(result)
- 가장 큰 화폐 단위부터 돈을 거슬러 주는 것.
- 화폐의 종류가 K개 라고 할 때 위 소스코드의 시간복잡도는 O(K)
- 즉, 동전의 총 종류에만 영향, 거슬러 줘야 하는 금액의 크기는 무관하다.
- 위 문제를 그리디 알고리즘으로 해결할 수 있는 이유는 가지고 있는 동전 중에서 큰 단위가 항상 작은 단위의 배수이므로 작은 단위의 동전들을 종합해 다른 해가 나올 수 없기 때문이다.
<aside> 💡
대부분의 그리디 알고리즘 문제에서는 이처럼 문제 풀이를 위한 최소한의 아이디어를 떠올리고 이것이 정당한지 검토할 수 있어야 답을 도출할 수 있다.
</aside>
실전 문제. 큰 수의 법칙
# 배열의 크기 N, 숫자가 더해지는 횟수 M, K+1번 초과할 수 없음. N, M, K = map(int,input().split()) arr = list(map(int,input().split())) arr.sort(reverse=True) #print(arr) # K가 1이여도 2개만 필요하다. (0,1 번째 인덱스) # 만약에 M < K 이면 제일 큰 수 x K 하면 된다. result = 0 if M <= K: result = arr[0] * M else: # M // K 만큼 (큰 수 * K 번) 반복됨. + (M % K)번 만큼 두 번째 인덱스를 더함. result = (arr[0] * K) * (M//K) + (M % K) * arr[1] print(result)
- 책에는 다른 풀이 1 가지와 유사한 풀이 1가지 총 2가지가 있다.
- 여기서 포인트는 3가지 정도 있다.
- K가 M 크거나 같을 때와 그 외로 나눌 수 있다.
- K ≥ M 이라면 큰 수를 더하는 횟수만큼 반복할 수 있기 때문이다.
- 크다면 중간 중간 숫자를 섞어줘야 한다.
- max() 를 사용하지 않은 이유는 서로 다른 인덱스에 해당하는 수가 같은 경우에도 서로 다른 것으로 간주하기 때문이다.
- 따라서 정렬을 한 뒤 인덱스를 사용해 해결했다.
- 수학적 수식으로 표현할 줄 알아야 했다. 어떻게 이 과정이 이루어질까 고민을 하면 충분히 가능했을 것이다.
실전 문제. 숫자 카드 게임
N, M = map(int, input().split()) result = 0 for _ in range(N): data = list(map(int, input().split())) # 1 min_value = min(data) # 2 result = max(result, min_value) # 3 print(result)
- 처음 든 생각은 리스트컨프리헨션을 사용해 2차원 배열을 초기화 해야될 까 생각했지만, 굳이 그럴 필요까지는 없다고 생각이 들었다. 그 이유는 각 행을 반복하며 가장 작은 수를 찾고, 찾은 수들 중 가장 큰 값만 반환하면 되기 때문이다.
- 여기서 포인트는 result = max(result, min_value) 문장을 이해했느냐이다.
- 설명을 하자면,
- 1 번째 문장은 각 행의 숫자 카드를 넣어주는 것이다.
- 2 번째 문장은 각 행에서 가장 작은 수를 찾는 것.
- 3 번쨰 문장은 result 값에 그 전의 숫자와 비교해 더 큰 값을 넣어주는 것이다.
실전 문제. 1이 될 때까지
# 어떤 수 N과 나눌 수 K N, K = map(int,input().split()) count = 0 while True: if N == 1: break if N % K == 0: N //= K else: N -= 1 count += 1 print(count)
- 여기서 파이썬에서 무언갈 종료하는 함수가 3가지 있다.
- break
- 루프 내에서 사용: break는 루프(반복문)를 중단하고 루프 밖으로 빠져나올 때 사용됩니다.
- 역할: 루프를 강제로 종료합니다. 조건에 맞을 때 반복을 멈추고 다음 코드를 실행할 수 있게 합니다.
- 사용 예시
for i in range(10): if i == 5: break # i가 5일 때 루프를 종료합니다. print(i) # 0부터 4까지만 출력됩니다.
- exit
- 프로그램 종료: exit()는 Python 프로그램을 완전히 종료할 때 사용됩니다.
- 역할: 프로그램 실행을 즉시 중단하고 시스템에 종료 신호를 보냅니다.
- 사용 예시
import sys if some_condition: sys.exit() # 프로그램이 종료됩니다.
- return
- 함수 내에서 사용: return은 함수에서 값을 반환하고 함수를 종료합니다.
- 역할: 함수의 실행을 중단하고, 호출된 지점으로 값을 돌려줍니다.
- 사용 예시
def add(a, b): return a + b # 함수 실행을 종료하고 결과를 반환 result = add(3, 5) # result는 8이 됩니다.
- break
- 몇 번 반복할지 모르니 while 문을 사용해주었다.
- N 과 K 의 초기 값은 항상 2 이상 이므로 N == 1 일 때 break 해주어 영원히 반복되지 않게 하였다. (base case)
- 재귀 함수로도 구현 가능할 거 같아 시도해보겠다.
N, K = map(int,input().split()) count = 0 def find(num): global count # base case if num == 1: return count elif num % K == 0: find(num // K) else: find(num-1) count += 1 print(find(N))
- 출력 결과로 None 값이 나온다.
- return 을 해주어야 재귀 호출이 끝나면 그 값을 상위 함수로 전달하여 계속해서 값을 반환하도록 하기 위함이다. return 을 하지 않으면 재귀 호출의 결과가 상위 함수로 전달 되지 않는다.
- 그리고 조건마다 count += 1 을 해주어야 된다. 그 이유는 조건이 실행될 때 마다 그 수를 계산하기 위해서 이다.
- 소스 코드를 아래와 같이 수정해야 된다.
N, K = map(int,input().split()) count = 0 def find(num): global count # base case if num == 1: return count if num % K == 0: count += 1 return find(num // K) else: count += 1 return find(num-1) print(find(N))
- 2가지 방법의 소스 코드들의 핵심은 최대한 많이 나누기이다. 그 이유는 문제에서 N이 1이 될 때까지 최소 횟수를 구하는 프로그램을 작성하도록 되어 있기 때문이다.
- 특정 양의 정수에서 최대한 많이 나누어야 최소 횟수가 되기 때문이다.
- N과 K가 2 이상의 자연수이기 때문.
본문의 내용은 아래 책에서 더 자세하게 보실 수 있습니다.
https://www.yes24.com/Product/Goods/91433923
이것이 취업을 위한 코딩 테스트다 with 파이썬 - 예스24
나동빈 저자의 유튜브 라이브 방송 https://www.youtube.com/c/dongbinnaIT 취준생이라면 누구나 입사하고 싶은 카카오 · 삼성전자 · 네이버 · 라인!취업의 성공 열쇠는 알고리즘 인터뷰에 있다!IT 취준생
www.yes24.com
'크래프톤정글' 카테고리의 다른 글
크래프톤 정글 6기 합격후기 [feat. 근데 6기 아님] (0) 2024.08.07