Programmers 172927.광물 캐기
문제 풀이 과정을 공유하고자 한다.
문제 출처
1. 문제설명
- 곡괭이로 광물을 캔다.
- 곡괭이는 총 3가지로 각각의 피로도는 고정되어 있다.
- 모든 곡괭이는 5개의 미네랄을 캘수 있음.
- 5개를 캐는 중간에 다른 곡괭이를 사용할 수 없음.
- 미네랄의 순서가 주어지고, 이 순서는 변경할 수 없다.
- 미네랄은 최대 50개, 곡괭이는 각각 최대 5개
2. 알고리즘 설계
- 문제를 보자마자 직감했다. 이건 그리디.
- 문제는 광물의 순서가 고정되어 있어 순차적으로 탐욕 접근이 불가능
- 곡괭이를 집어들면, 무조건 5개의 미네랄을 캐야한다.
- 5개씩 쪼개면 순서를 지키지 않아도 된다!
- 피로도가 높은 5개의 광물을 다이아 곡괭이부터 소모시키는 게 가장 이상적
- 피로도 순으로 정렬하면 되겠구나!
- 이전 문제에서 사용했던
priority_queue
(pq) 사용
- 구조체 정의
- 피로도의 총합이 큰 순이므로,
operator
의 부호가 일치한다.
struct Weight { int sum; int arr[3]; Weight(int dia, int iron, int stone) { sum = dia + iron + stone; arr[0] = dia, arr[1] = iron, arr[2] = stone; } bool operator< (const Weight &other) const { return sum < other.sum; } };
- 피로도의 총합이 큰 순이므로,
3. 로직
- 미네랄의 길이보다, 곡괭이 사용횟수가 적을 수도 있다.
- 이걸 안해주면, 못 캐는 광물까지 5개로 쪼개서 그리디를 해버리게 된다…!
int len = (picks[0]+picks[1]+picks[2])*5; len = (len <= minerals.size()) ? len : minerals.size();
- 5개씩 광물을 쪼갠다.
- 마지막 쪼가리(?)는 5개보다 적을 수 있다.
for(int i=0; i<len; i+=5) { for(int j=i; j<min(len, i+5); j++) { string cur = minerals[j];
- 탐색
- 크기 3의 배열[다이아몬드, 철, 돌]에 각 피로도 저장
- 각 피로도의 총합을 이용해 pq에 저장
- pq에서 하나씩 꺼내면서 다이아몬드 곡괭이부터 소모시킨다.
4. 전체 코드
5. 소감
- 문제를 읽자마자 이건 그리디였다.
- 그러나, 광물을 캐는 순서를 지켜야 하기 때문에 순차 접근으로는 그리디가 불가능했다.
-
5개씩 쪼개면 순서 안 지켜도 된다는 건.. 어떤 분의 힌트를 보았기 때문이다..
- 본인은 곡괭이의 피로도를 일일히 입력
- 코드를 보면 14줄에 걸쳐 숫자를 더한다.
unordered_map
을 이용해 한줄로 만들 수 있음..unordered_map<string, int> dic[3] = { { { "diamond", 1 }, { "iron", 1 }, { "stone", 1 } }, { { "diamond", 5 }, { "iron", 1 }, { "stone", 1 } }, { { "diamond", 25 }, { "iron", 5 }, { "stone", 1 } } };