👉🏻 그리디 알고리즘
- 그리디 알고리즘(탐욕법)은 현재 상황에서 지금 당장 좋은 것만 고르는 방법을 의미한다
- 일반적인 그리디 알고리즘은 문제를 풀기 위한 최소한의 아이디어를 떠올릴 수 있는 능력을 요구한다
- 그리디 해법은 그 정당성 분석이 중요하다
- 단순히 가장 좋아 보이는 것을 반복적으로 선택해도 최적의 해를 구할 수 있는지 검토한다
- greedy: 탐욕적인
- 크루스칼 알고리즘, 다익스트라 알고리즘을 제외하고 그리디 알고리즘이 출제 되면 해당 문제를 풀 수 있는 최소한의 아이디어를 적절히 떠올릴 수 있어야 문제가 풀리도록 출제되는 경우가 많다
🧩 문제 상황
- 루트 노드부터 시작하여 거쳐 가는 노드 값의 합을 최대로 만들고 싶다. 이 때 최적의 해는?
- 5 + 7 + 9 가 가장 크다
✅ 단순히 매 상황에서 가장 큰 값만 고른다면 어떻게 될까?
- 루트노드에서 인접한 노드는 7, 10, 8이다. 이 때 최대값은 10이므로 10을 선택한다
- 그 다음 10에서 인접한 노드는 4, 3이다. 이때 최대값은 4이므로 4를 선택한다
- 그럼 최종적으로 총합 19가 나온다. 최적의 해인 21보다 작은 수이다
- 이처럼 단순히 매 상황에서 가장 큰 값만 고르는 방식
- 일반적인 상황에서 그리디 알고리즘은 최적의 해를 보장할 수 없을 때가 많다
- 실제 프로그램을 개발할 때는 그리디 알고리즘을 써도 충분히 최적의 해에 가까운 값을 얻을 수 있거나 최적의 해를 얻을 수 있을 때 그리디 알고리즘을 사용
- 하지만 코딩테스트에서의 대부분의 그리디 문제는 탐욕법으로 얻은 해가 최적의 해가 되는 상황에서, 이를 추론할 수 있어야 풀리도록 출제된다
- 출제자가 입출력값을 정해놓고 문제를 만든다 → 그리디 알고리즘을 사용해도 된다고 추론할 수 있어야 문제가 풀리도록 의도
🧩 문제
- 최적의 해를 빠르게 구하기 위해서는 가장 큰 화폐 단위부터 돈을 거슬러주면 된다
- N운을 거슬러 줘야 할 때, 가장 먼저 500원으로 거슬러 줄 수 있을 만큼 거슬러준다
- 이후에 100원, 50원, 10원짜리 동전을 차례대로 거슬러 줄 수 잇을 만큼 거슬러 주면 된다.
✅ N = 1,260일 때 예시를 확인해보자
남은 돈💵 : 1,260
1️⃣ 500원짜리 먼저 줌
남은 돈💵 : 260
2️⃣ 100원짜리 줌
남은 돈💵 : 60
3️⃣ 50원짜리 줌
남은 돈💵 : 10
4️⃣ 10원짜리 줌
- 단순히 가장 큰 화폐부터 거슬러 주는 것으로 최적의 해를 보장할 수 있는 이유?
✅ 정당성 분석
- 가장 큰 화폐 단위부터 돈을 거슬러 주는 것이 최적의 해를 보장하는 이유는 무엇일까?
- 가지고 있는 동전 중에서 큰 단위가 항상 작은 단위의 배수이므로 작은 단위의 동전들을 종합해 다른 해가 나올 수 없기 때문이다
- 만약 800원 거슬러 주어야 하는데 화폐 단위가 500원, 400원, 100원이라면?
- 알고리즘에 따르면 500원 하나 + 100원 3개
- 하지만 400원짜리 두 개 거슬러 주는 게 답
- 즉, 큰 단위가 작은 단위의 배수가 아니라면 이 알고리즘을 통해 최적의 해를 보장할 수 없다
- 그리디 알고리즘 문제에서는 이처럼 문제 풀이를 위한 최소한의 아이디어를 떠올리고 이것이 정당한지 검토할 수 있어야 한다.
💡 답안 예시
- 큰 단위부터 리스트에 담아줌
- 각각의 동전을 확인하면서 결과값에 n을 coin으로 나눈 몫을 담는다
- 현재 남아 있는 돈을 현재의 동전으로 최대한 많이 거슬러주도록
- 남은 돈은 n을 coin으로 나눈 나머지
✅ 시간 복잡도 분석
- 화폐의 종류가 K라고 할 때, 소스코드의 시간 복잡도는 O(K)이다
- 이 알고리즘의 시간 복잡도는 거슬러줘야 하는 금액과는 무관하며, 동전의 총 종류에만 영향을 받는다
✅ 다른 언어 답안 보기
- C++
- 표준 라이브러리 불러오기
- C++에서는 전역변수는 자동으로 0으로 초기화됨
- 배열에 동전 종류를 담음
- 반복문 (의미는 python과 같음)
- Java
- 클래스 명을 Main으로 요구하는 경우가 많음
👉🏻 문제1 : 1이 될 때 까지
🧩 조건
💡 스스로 풀어보기
- 일단 답은 나왔으나 그리디 알고리즘을 제대로 활용했는지는 모르겠다
🧩 문제 해결 아이디어
- 주어진 N에 대하여 최대한 많이 나누기를 수행하면 된다
- N의 값을 줄일 때 2 이상의 수로 나누는 작업이 1을 빼는 작업보다 수를 훨씬 많이 줄일 수 있다
- 예를 들어 N = 25, K = 3일 때 다음과 같다
🧩 정당성 분석
- 가능하면 최대한 많이 나누는 작업이 최적의 해를 항상 보장할 수 있을까?
- N이 아무리 큰 수여도, K로 계속 나눈다면 기하급수적으로 빠르게 줄일 수 있다
- 다시 말해 K가 2 이상이기만 하면, K로 나누는 것이 1을 빼는 것 보다 항상 빠르게 N을 줄일 수 있다
- 또한 N은 항상 1에 도달하게 된다. (최적의 해 성립)
💡 예시 답안
- 입력받은 문자열을 공백을 기준으로 나누고 map 함수를 이용해서 각각 int형으로 만들어준다
- target에 n을 k로 나눈 값에 k를 다시 곱한 값을 넣어준다.
- n이 k로 나누어 떨어지지 않을 때 가장 가까운 k로 나누어떨어지는 수가 어떤 것인지를 찾고자 할 때 사용할 수 있다?
(예를 들어 n이 17이고 k가 5라면 15가 됨)
- 즉, n에서 1을 빼는 과정을 몇번 반복해서 target이라는 값 까지 만들 수가 있고
- target이라는 값은 k로 나누어 떨어지는 수가 된다.
(N 안에서 나누기를 최대한 많이 이용해야하기 때문에 N보다 작은 수 중 k로 나누어떨어지는 가장 큰 수를 만드는 과정인 듯)
- 총 연산 횟수인 result에 1을 빼는 연산 횟수를 넣어준다. n에서 target을 뺀 값 만큼 1을 빼줘야 한다 (한번에)
- n이 k보다 작다면 반복문을 탈출한다
- 작지 않다면 n을 k로 나눠준다
- k로 나누는 연산을 한 번 수행하기 때문에 result에 1을 더해준다
- n이 1보다 크다면 1이 될 수 있도록 만들기 위해 남은 수에 대해 1씩 빼는 연산 수행
- n과 k가 10만보다 작은 정수이기 때문에 그냥 매번 n을 확인해서 n이 k로 나누어 떨어진다면 나눠주고 아니라면 1을 빼줘도 됨
- 하지만 위와 같이 작성할 경우 반복문 한 번에 n을 k로 나누는 연산이 한 번 이루어짐 반복 횟수에 다라 n이 기하급수적으로 줄어든다
- 시간복잡도가 log시간복잡도가 나올 수 있음
* C++ ver
*Java ver
👉🏻 문제2: 곱하기 혹은 더하기
- 20억 이하의 정수인 이유: 일반 프로그래밍 언어에서 정수 데이터를 위해 기본 int형을 사용할 경우 약 21억까지 가능
- 사실 파이썬에서는 기본적으로 정수 데이터를 처리함에 있어서 수의 범위 제한이 없다
🧩 조건
💡 스스로 풀어보기
- 일단 정답이 나오긴 함
🧩 문제 해결 아이디어
- 대부분의 경우 '+'보다는 'x'가 더 값을 크게 만든다
- 예를 들어 5 + 6 = 11, 5 × 6 = 30
- 다만 두 수 중에서 하나라도 '0'혹은 '1'인 경우, 곱하기 보다는 더하기를 수행하는 것이 효율적이다
- 따라서 두 수에 대하여 연산을 수행할 때, 두 수 중에서 하나라도 1 이하인 경우에는 더하고 두 수가 모두 2 이상인 경우에는 곱하면 정답이다
💡 예시 답안
- 숫자로만 구성된 하나의 문자열을 입력받는다
- 첫 번째 문자를 숫자로 변경하여 result에 대입한다
- 두 번째 숫자부터 차례로 확인하면서
- 두 수(result와 num)중 하나라도 0혹은 1이라면 더하기, 아니라면 곱하기를 수행한다
* C++ ver
* Java ver
👉🏻 문제3: 모험가 길드
🧩 문제 설명
🧩 조건
💡 스스로 풀어보기
- 완성하지 못함
- 내림차순으로 정렬하여 인원을 많이 필요로 하는 것 부터 해결해야된다고 생각했음
- 만약 공포도가 1인 경우 혼자 남아도 혼자 팀을 꾸릴 수 있기 때문에
🧩 문제 해결 아이디어
💡 예시 답안
- count는 그룹에 포함된 멤버수로 그룹이 결성이 되면 0으로 다시 돌아와야 한다
- 일단 현재 그룹에 모험가 한 명 포함시키기
- 만약 현재 그룹에 포함된 모험가의 수 >= 현재 공포도 라면 그룹 결성
- if에 해당하지 않는다면(현재 그룹에 포함된 모험가의 수 < 현재 공포도) count += 1을 계속 실행
* C++ ver
*Java ver
출처: https://youtu.be/5OYlS2QQMPA?si=S8aWBbiYyVJZJ3xl
https://youtu.be/_TG0hVYJ6D8?si=jouig9-Vec3QmFP6
'Python > 이것이코딩테스트다with파이썬' 카테고리의 다른 글
16강 스택과 큐 자료구조 (0) | 2023.11.21 |
---|---|
14~15강 :: 구현 유형 개요 및 문제 풀이 (0) | 2023.11.15 |
11강 :: 자주 사용되는 표준 라이브러리 (2) | 2023.11.09 |
10강 :: 함수와 람다 표현식 (0) | 2023.11.07 |
8~9강 :: 조건문, 반복문 (0) | 2023.11.02 |