일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 정렬알고리즘
- css
- isDisable
- YOLOv5
- 데이터셋
- 직접 라벨링
- springboot
- 데이터셋 직접
- 스프링부트
- 모델 훈련
- 게시판만들기
- React
- 객체 감지
- 게시판
- HTML공부
- 정처기 실기
- 정처기
- 컴활1급필기
- 방만들기
- object detection
- combobox
- thymeleaf-layout-dialect
- 글 검색
- labelImg
- html
- 조회수 증가
- customized yolov5
- 이것이 취업을 위한 코딩 테스트다 with 파이썬
- 드라이브 마운트
- 욕심쟁이 알고리즘
- Today
- Total
기록장
알고리즘 : 그리디 Greedy (탐욕법, 욕심쟁이 알고리즘) 본문
본 글은 내가 '이것이 코딩테스트다 with 파이썬' 이라는 도서를 읽고 기록용으로 정리하는 것이다. 1회독을 할 때마다 글을 수정할 수도 있다.. (***내가 이해하기 쉽게 정리해놓은 글)
1회독
그리디 알고리즘이란??
그리디는 단순 무식하게, 탐욕적으로 문제를 푸는 알고리즘
여기서 탐욕적이라는 것은 현재 상황에서 지금 당장 좋은 것만 고르는 방법이다.
매 순간 가장 좋아 보이는 것을 선택하며 이 선택이 나중에 미칠 영향에 대해서는 생각하지 않는다.
그리디 알고리즘은 기준에 따라 좋은 것을 선택하는 알고리즘으로 코딩테스트 문제에서 '가장 큰 순서대로', '가장 작은 순서대로'와 같이 알게 모르게 기준을 제시해준다. 대체로 이러한 기준은 정렬 알고리즘을 사용했을 때 만족시킬 수 있으므로 그리디 알고리즘은 정렬 알고리즘과 함께 출제된다.
예제) 거스름돈 문제
거스름돈으로 사용할 수 있는 돈 : 500원, 100원, 500원, 10원 짜리 동전 무한개
거슬러 줘야 할 돈이 N원일 때 거슬러 줘야 할 동전의 최소 개수를 구하라 (단, N은 항상 10의 배수)
A. 가장 큰 화폐 단위부터 거슬러 준다.
예를 들어, 500원 짜리를 거슬러 줄 수 있는 만큼 최대한 거슬러 주고, 그 다음 100원 그 다음 500원 ..
1) N이 1280원일 때 500원으로는 1000원을 만들 수 있다 ( 1280 / 500 의 몫 ) : 500원은 2개
2) 1번의 결과 나머지를 다시 100으로 나누어준다. ( 280 / 100 의 몫 ) : 100원 2개
3) 2번의 결과 나머지를 다시 50원으로 나누어준다. ( 80 / 50 의 몫) : 50원 2개
4) 3번의 결과 나머지를 다시 10원으로 나누어준다. (30 / 10 의 몫) : 10원 3개
1~4번까지를 보면 반복적으로 몫은 동전의 개수가 되고 나머지는 다시 다음 단위로 나누게 된다.
이를 정리하면 몫은 동전의 개수 변수에 더해주면 되고 나머지는 다시 N에 저장하면 될 것처럼 생각이 든다.
n = 1280
coin_sum = 0
coin_type = [500, 100, 50, 10]
for coin in coin_type:
coin_sum += n / coin # 몫은 그 cointype의 개수 -> coin_sum에 더해준다
n %= coin # 나머지는 다음 계산을 위해 다시 n에 넣어준다
print(coin_sum)
실전1) 큰 수의 법칙
여기서 말하는 큰 수의 법칙은 다양한 수로 이루어진 배열이 있을 때 주어진 수들을 M번 더하여 가장 큰 수를 만드는 법칙이다. 하지만 어떤 한 수를 연속적으로 더할 수 있는 횟수는 최대 K번이다.
배열의 크기 - N
숫자를 더하는 횟수 - M
한 수를 연속적으로 더할 수 있는 최대 횟수 - K
일 때, N=5, M=8, K=3으로 주어지고 배열이 [ 2, 4, 5, 4, 6 ] 이라고 해보자.
그러면 가장 큰 수를 만들기 위해서는 배열에서 제일 큰 수를 더해야 한다고 생각이 들 것이다. 그렇다면 6을 선택해서 더하게 될텐데 6 + 6 + 6 을 하면 최대 횟수(3번)이 되었기 때문에 더이상 6을 더할 수 없다. 그렇다면 다음번으로 큰 수 5를 선택하여 더한다. (6 + 6 + 6 + 5) 6을 연속적으로 4번 더할 수 없는 것이고 5를 한번 더했기 때문에 다시 6을 더할 수 있다. 이러한 방식으로 8번을 더하면 6 + 6 + 6 + 5 + 6 + 6 + 6 + 5 = 46, 답은 46이 된다.
이것을 그럼 코드로 작성해보자.
* 단순 코드 *
N=5,M=8, K=3
datalist = [2,4,5,4,6]
#제일 큰 수를 찾기 위해 sort
datalist.sort()
firstN = datalist[N-1] # 오름차순 정렬이기 때문에 가장 큰 수는 마지막 인덱스에 저장
secondN = datalist[N-2] # 그 다음 큰 수는 마지막인덱스-1 에 저장
sum = 0 // 합계 변수
while True :
for i in range(K): # K번만큼 가장 큰 수를 더해준다
if M == 0:
break; # M이 0이 되면 루프를 빠져나온다.
sum +=firstN
M = M-1 #더하는 횟수를 한번 뺀다
if M == 0:
break; # M이 0이 되면 루프를 빠져나온다.
sum+=secondN #두번째로 큰 수 더하기
M = M-1 #더하는 횟수를 한번 뺀다
근데 이 방법은 M의 크기가 100억 이상으로 커지면 시간 초과 판정을 받게 된다. "반복되는 수열" 을 파악해서 코드를 작성할 수 있다.
위에서 6 + 6 + 6 + 5 + 6 + 6 + 6 + 5 = 46 으로 문제를 해결할 수 있다고 하였는데 자세히 보면 6+6+6+5가 두번 반복된 것이다. (가장 큰 수 K개 그 다음 큰 수 1개) 가 하나의 세트라고 볼 수 있다. 그러면 총 이 세트는 K+1개 인데 M을 K+1로 나누었을 때 몫이 이 세트가 반복되는 횟수이다. 그럼 가장 큰 수는 K * (M/K+1) 번 계산된다. 이것은 M이 K+1로 나누어떨어질 때이고, 나머지가 있는 경우도 고려해야한다. 나머지의 수만큼 가장 큰 수가 더 더해져야하기 때문에 총 firstN이 더해지는 횟수는 K*(M/ (K+1))+ M % (K+1) 이다. 그렇다면 그 다음 큰 수 secondN은 K*(M/ (K+1)) 만큼 더하면 된다.
이것을 코드로 작성해보자
N=5,M=8, K=3
datalist = [2,4,5,4,6]
#제일 큰 수를 찾기 위해 sort
datalist.sort()
firstN = datalist[N-1] # 오름차순 정렬이기 때문에 가장 큰 수는 마지막 인덱스에 저장
secondN = datalist[N-2] # 그 다음 큰 수는 마지막인덱스-1 에 저장
sum = 0 # 합계 변수
# firstN가 더해지는 횟수
count = int(M/(K+1)) * K
count += M % (K+1) #나머지가 있을 때
#firstN을 count수 만큼 더하기
sum += count * firstN
sum += (M-count) * secondN # 나머지 횟수는 두번째로 큰 수를 더한다.
'코딩테스트 공부' 카테고리의 다른 글
파이썬 List comprehension (0) | 2021.08.14 |
---|---|
파이썬 자료구조 stack과 queue (0) | 2021.08.13 |
파이썬 해시(딕셔너리) (0) | 2021.08.12 |
코테에서 자주 사용하는 라이브러리,함수 모음 (계속 추가 예정) (0) | 2021.08.11 |
자주하는 파이썬 실수 *****제발 실수 하쥐마라***** (기억하려고) (0) | 2021.07.12 |