-
프로그래머스 문제 풀이 더 맵게24년 11월 이전/레거시-프로그래머스-코딩 테스트 고득점 kit 2019. 11. 16. 16:01반응형
이 문제는 이시윤 강사님의 프로그래머스 강좌 "파이썬을 무기로, 코딩테스트 광탈을 면하자!"를 보고 정리한 내용입니다.
문제 URL 더 맵게
Contents
- 문제 지문 파악하기
- 강사님의 알고리즘 풀이
- 구르미의 알고리즘 풀이
문제 지문 파악하기
문제의 입력을 통해서, 문제를 어떻게 풀어야할지 생각해봅시다.
문제 입력 :
scoville = [1, 2, 3, 9, 10, 12]
K=7scoville에서 K보다 작은 수는 1, 2, 3이 존재합니다. 따라서 scoville을 섞어주어야 합니다. scoville에서 가장 작은 원소 1과, 두번째로 작은 원소 2를 꺼냅니다. 그 후 다음 수식을 적용시켜서 값을 저장합니다.
수식 : 가장 작은 원소 + (두 번째로 작은 원소 * 2)
즉, 수식을 적용하면 "1 + 2*2 = 5"가 되어 5라는 값을 다시 힙에 저장시키는 것이죠. 힙의 상태는 다음과 같습니다.
현재 상태 :
scoville = [3, 5, 9, 10, 12]
섞은 횟수 = 1번아직 7보다 작은 수가 scoville에 존재합니다. 따라서 1번 더 섞도록 하죠. 이때, first = 3, second = 5이기 때문에 13(3 + 5 * 2)을 저장하게 됩니다.
현재 상태 :
scoville = [9, 10, 12, 13]
섞은 횟수 = 2번이제 scoville에서 K인 7보다 작은 수는 없습니다. 따라서 답 answer는 총 섞은 횟수 2를 반환하면 됩니다. 여기서 중요한 점은 데이터를 저장하고 삭제하는 과정에서 정렬 상태를 유지해야한다는 것입니다. 그럼 섞을 때마다, 정렬을 해주면 되겠군요! 즉, 다음처럼 풀수 있겠지요.
def solution(scoville, K): answer = 0 # 1. scoville 정렬 scoville.sort() # 2. 스코빌 지수 섞기 while scoville: first = scoville.pop(0) if first >= K: break if len(scoville) <= 0: return -1 second = scoville.pop(0) scoville.append(first + second * 2) scoville.sort() answer += 1 return answer
이렇게 하면, 정확성 테스트는 모두 통과하지만, 효율성 테스트에서 모두 "시간 초과"라는 결과를 얻을 수 있습니다. 이럴 때, 시간 복잡도는 O(N * Nlog(N))입니다. 왜냐하면 최대 scoville 길이만큼 반복해야하는데, 매 반복시, 정렬을 해주어야하기 때문입니다.
참고!
정렬 알고리즘의 시간 복잡도는 O(Nlog(N))입니다.다른 방법이 필요합니다. 우리는 데이터 저장/삭제 시 정렬을 유지하는 자료구조를 알고 있습니다. 맞습니다. "힙" 혹은 "우선 순위 큐"를 이용하여 문제를 해결하면 됩니다.
강사님의 알고리즘 풀이
강사님의 알고리즘을 순서대로 표현하면 다음과 같습니다.
- scoville을 토대로, 최소 힙을 생성하여 초기화합니다.
- 문제의 조건대로 scoville을 섞습니다. 아래의 반복 조건들을 만족할 때까지 다음을 반복합니다.
- 힙의 첫번째 요소(가장 작은 원소) first 를 꺼냅니다.
- 힙의 두번째 요소(두번째로 작은 원소) seond를 꺼냅니다.
- 문제 조건에 따라서, 힙에 first + second * 2를 저장합니다.
- answer를 1 올립니다.
여기서 반복 조건들은 다음과 같습니다.
- 힙이 비어있지 않아야 합니다.
- 힙의 첫 원소가 K보다 작아야 합니다.
- 힙에서 두 번째 원소를 꺼낼 수 있어야 합니다. 3번을 만족시키지 못하면 아무리 섞어도 힙의 가장 작은 원소가 K를 넘을 수 없습니다.
이를 코드로 표현하면 다음과 같습니다.
def solution(scoville, K): import heapq answer = 0 # 1. 힙 생성 및 초기화 heapq.heapify(scoville) # 2. 스코빌 지수 섞기 # 반복 조건 1. 힙이 비어있지 않아야 한다. # 반복 조건 2. 힙의 첫 원소(가장 작은 수)가 K 보다 작아야 한다. # 반복 조건 3. 힙에서 두 번째 원소를 꺼낼 수 있어야 합니다. 없다면, -1을 반환합니다. # 2-1. 힙의 첫 번째 요소 first 꺼냄 # 2-2. 힙의 두 번째 요소 second 꺼냄 # 2-3. 힘에 first + second * 2 를 저장함 # 2-4. answer를 1 증가시킴. # 2-5. 조건들을 만족할 때까지 1-4 반복 while scoville: first = heapq.heappop(scoville) if first >= K: break if len(scoville) <= 0: return -1 second = heapq.heappop(scoville) heapq.heappush(scoville, first + second * 2) answer += 1 return answer
힙의 저장과 삭제 연산은 O(log(N))의 시간 복잡도를 가집니다. 또한, 문제에서 최악의 경우, scoville 길이 N만큼 반복하기 때문에, 위 알고리즘의 시간 복잡도는 O(N * log(N))입니다. 실제 코드를 제출하면 정확성 테스트까지 모두 통과하는 것을 확인할 수 있습니다.
구르미의 알고리즘 풀이
제 풀이 역시 강사님과 다르지 않습니다. 파이썬의 경우는 완전히 같기 때문에, CPP 코드만 보여드리겠습니다. CPP 코드는 다음과 같습니다.
#include <string> #include <vector> #include <queue> using namespace std; int solution(vector<int> scoville, int K) { int answer = 0; // 1. 우선순위 큐(최소 힙) 생성 priority_queue<int, vector<int>, greater<int>> pq; // 2. 우선순위 큐(최소 힙) 초기화 for (const auto & elem : scoville) { pq.push(elem); } // 3. 스코빌 지수 섞기 // 반복 조건 1. 힙이 비어있지 않아야 한다. // 반복 조건 2. 힙의 첫 원소(가장 작은 수)가 K 보다 작아야 한다. // 반복 조건 3. 힙에서 두 번째 원소를 꺼낼 수 있어야 합니다. 없다면, -1을 반환합니다. // 3-1. 첫번째 원소 first 꺼냄 // 3-2. 두번째 원소 second 꺼냄 // 3-3. first + second * 2 저장 // 3-4. answer 1 추가 // 3-5. 조건들을 만족할 때까지 1-4 반복 while (!pq.empty()) { int first = pq.top(); pq.pop(); if (first >= K) { break; } if (pq.empty()) { return -1; } int second = pq.top(); pq.pop(); pq.push(first + second * 2); answer += 1; } return answer; }
728x90'레거시 > 레거시-프로그래머스-코딩 테스트 고득점 kit' 카테고리의 다른 글
프로그래머스 문제 풀이 여행 경로 (0) 2019.11.25 프로그래머스 문제 풀이 N으로 표현 (20) 2019.11.24 프로그래머스 문제 풀이 큰 수 만들기 (6) 2019.11.06 프로그래머스 문제 풀이 가장 큰 수 (2) 2019.11.06 프로그래머스 문제 풀이 체육복 (0) 2019.11.05