프로그래머스 | Level 2 | 마법의 엘리베이터
Programmers | Level 2 | 마법의 엘리베이터
Problems
- 📘 Src: Programmers
- 🗂️ Cat: Greedy
Description
마법의 세계에 사는 민수는 아주 높은 탑에 살고 있습니다. 탑이 너무 높아서 걸어 다니기 힘든 민수는 마법의 엘리베이터를 만들었습니다. 마법의 엘리베이터의 버튼은 특별합니다. 마법의 엘리베이터에는 -1, +1, -10, +10, -100, +100 등과 같이 절댓값이 10c (c ≥ 0 인 정수) 형태인 정수들이 적힌 버튼이 있습니다. 마법의 엘리베이터의 버튼을 누르면 현재 층 수에 버튼에 적혀 있는 값을 더한 층으로 이동하게 됩니다. 단, 엘리베이터가 위치해 있는 층과 버튼의 값을 더한 결과가 0보다 작으면 엘리베이터는 움직이지 않습니다. 민수의 세계에서는 0층이 가장 아래층이며 엘리베이터는 현재 민수가 있는 층에 있습니다.
마법의 엘리베이터를 움직이기 위해서 버튼 한 번당 마법의 돌 한 개를 사용하게 됩니다.예를 들어, 16층에 있는 민수가 0층으로 가려면 -1이 적힌 버튼을 6번, -10이 적힌 버튼을 1번 눌러 마법의 돌 7개를 소모하여 0층으로 갈 수 있습니다. 하지만, +1이 적힌 버튼을 4번, -10이 적힌 버튼 2번을 누르면 마법의 돌 6개를 소모하여 0층으로 갈 수 있습니다.
마법의 돌을 아끼기 위해 민수는 항상 최소한의 버튼을 눌러서 이동하려고 합니다. 민수가 어떤 층에서 엘리베이터를 타고 0층으로 내려가는데 필요한 마법의 돌의 최소 개수를 알고 싶습니다. 민수와 마법의 엘리베이터가 있는 층을 나타내는 정수 storey
가 주어졌을 때, 0층으로 가기 위해 필요한 마법의 돌의 최소값을 return
하도록 solution
함수를 완성하세요.
Constraints
- 1 ≤
storey
≤ 100,000,000
Examples
storey | result |
---|---|
16 | 6 |
2554 | 16 |
Solutions
Code
import java.util.*;
class Solution {
public int solution(int storey) {
int stone = 0; // 0층으로 가기 위해 필요한 마법의 돌 개수를 기록하는 변수
while (storey > 0) {
int ones = storey % 10; // 현재 층수의 1의 자리 숫자
int tens = (storey / 10) % 10; // 현재 층수의 10의 자리 숫자
if (ones > 5 || (ones == 5 && tens >= 5)) { // 올림
storey = (storey / 10) + 1; // 현재 자릿수를 올림하여 다음 자릿수로 이동
stone += (10 - ones); // 올림을 위해 필요한 마법의 돌 개수를 추가
} else { // 내림
storey = storey / 10; // 현재 자릿수를 내림하여 다음 자릿수로 이동
stone += ones; // 내림을 위해 필요한 마법의 돌 개수를 추가
}
}
return stone; // 계산된 최소 마법의 돌 개수를 반환
}
}
Approach
이 문제는 주어진 층수에서 0층으로 내려가는 데 필요한 최소 마법의 돌 개수를 계산하는 문제입니다. 이 문제를 해결하기 위해 그리디 알고리즘(Greedy)을 사용하여 각 자리 숫자마다 최적의 선택을 반복합니다.
1. 문제 분석
이 문제는 주어진 층수에서 최소한의 버튼 누름으로 0층까지 내려가는 방법을 찾는 것입니다. 마법의 엘리베이터는 +1, -1, +10, -10 등 10의 배수로 작동하며, 중요한 점은 현재 층수에서 각 자릿수를 기준으로 올림을 할지, 내림을 할지 결정하는 것입니다. 이러한 선택을 통해 버튼 누름 횟수를 최소화할 수 있습니다.
우선, 올림을 선택하는 경우는 다음과 같습니다. 1의 자릿수가 5
보다 큰 경우, 예를 들어 6, 7, 8, 9
라면 내림보다는 올림을 통해 상위 자릿수에서 처리하는 것이 더 효율적입니다. 예를 들어 29
층에서 -9
를 하려면 9번의 버튼을 눌러야 하지만, +1
을 더해 30층으로 만든 후 -30
을 한 번에 처리하는 것이 더 적은 버튼을 누르는 방법입니다.
또한, 1의 자릿수가 5
인 경우에도 상황에 따라 올림을 선택하는 것이 유리할 수 있습니다. 특히 10의 자릿수가 5
이상이라면, 예를 들어 55
층에서 -5
를 하는 대신 +5
를 더해 60층으로 만든 후 -60
으로 한 번에 처리하는 것이 더 효율적입니다.
반대로, 내림을 선택하는 경우도 있습니다. 1의 자릿수가 5
보다 작은 경우, 예를 들어 1, 2, 3, 4
라면, 내림을 통해 바로 처리하는 것이 더 효율적입니다. 예를 들어 12
층에서 -2
를 하는 것이 가장 적은 버튼으로 0층에 도달할 수 있는 방법입니다.
이러한 올림과 내림의 기준을 통해 각 자리에서 최적의 선택을 반복함으로써 전체 버튼 누름 횟수를 최소화할 수 있습니다. 이 과정에서 가장 중요한 것은 다음 자릿수로 어떻게 넘길지를 결정하는 것이며, 각 자리에서 그 순간의 최선의 선택을 하는 것이 전체 최적해를 보장하기 때문에, 이 문제는 그리디 알고리즘으로 해결할 수 있습니다.
2. 접근 방식
이 문제를 해결하기 위해, 각 자리의 숫자에 대해 최적의 선택을 반복하는 그리디 알고리즘을 사용합니다.
- 현재 층수를 10으로 나누며, 1의 자리 숫자와 10의 자리 숫자를 기준으로 올림 또는 내림을 결정합니다.
- 1의 자리 숫자가 5보다 크거나, 1의 자리가 5이고 10의 자리가 5 이상인 경우 올림을 선택합니다.
- 그렇지 않은 경우에는 내림을 선택하여 가능한 빠르게 0층으로 이동합니다.
3. 초기 변수 설정
먼저, 0층까지 내려가기 위해 필요한 마법의 돌 개수를 기록할 변수를 초기화합니다. 이 변수는 최종적으로 최소 마법의 돌 개수를 반환하는 데 사용됩니다.
// 마법의 돌 개수를 세는 변수
int stone = 0;
4. 층수 계산을 위한 반복 구조
이제 storey
가 0층이 될 때까지 반복적으로 각 자리의 숫자를 분석하며, 올림 또는 내림을 통해 최적의 경로를 찾습니다. 이 과정에서 현재 층의 일의 자리와 십의 자리를 계산하여, 다음 단계에서 어떤 선택을 할지 결정합니다.
while (storey > 0) {
int ones = storey % 10; // 현재 층의 일의 자리 숫자
int tens = (storey / 10) % 10; // 다음 층의 일의 자리 숫자
...
}
ones
: 현재 층의 일의 자리를 계산하여, 올림 또는 내림의 기준이 됩니다. 예를 들어, 일의 자리가 크다면 올림을 통해 상위 자릿수에서 처리하는 것이 유리할 수 있습니다.tens
: 현재 층의 십의 자리를 계산하여, 특히 일의 자리가 5일 때 올림을 할지 내림을 할지 결정하는 데 중요한 역할을 합니다. 예를 들어, 십의 자리가 5 이상이라면 올림을 선택하는 것이 더 효율적일 수 있습니다.
이 두 변수는 매 반복마다 현재 층수에서 최적의 이동 방법을 결정하는 데 사용됩니다. 이를 통해 가장 효율적인 경로를 선택하고, 마법의 돌 사용을 최소화할 수 있습니다.
5. 올림 처리
현재 층의 일의 자릿수가 5보다 크거나, 일의 자릿수가 5이면서 십의 자릿수가 5 이상인 경우 올림을 선택하는 것이 더 효율적입니다. 올림을 통해 상위 자릿수에서 한 번에 큰 수를 처리할 수 있어, 전체적으로 필요한 돌의 개수를 줄일 수 있습니다.
while (storey > 0) {
...
// 올림
if (ones > 5 || (ones == 5 && tens >= 5)) {
storey = storey / 10 + 1; // 상위 자릿수로 올림하여 이동
stone += (10 - ones); // 10에서 현재 일의 자릿수를 뺀 값을 돌 개수에 추가
}
}
- 일의 자릿수가 5보다 크거나, 5이면서 십의 자릿수가 5 이상일 때, 올림을 통해 상위 자릿수에서 문제를 해결합니다. 예를 들어,
16
층에서+4
로20
층으로 만들어 한 번에 처리하는 것이,-6
을 6번 누르는 것보다 더 효율적입니다. - 올림을 하게 되면, 현재 층수를 10으로 나눈 값에 1을 더하여 상위 자릿수로 이동합니다. 이 과정에서
10 - 일의 자리
만큼의 돌을 추가로 사용해, 현재 자리의 수를 0으로 맞춥니다.
이렇게 함으로써, 작은 수를 반복적으로 처리하는 것보다 한 번에 큰 수를 처리하여 마법의 돌 사용을 최소화할 수 있습니다.
6. 내림 처리
반면, 현재 층의 일의 자릿수가 5보다 작거나, 일의 자릿수가 5이면서 십의 자릿수가 5 미만인 경우 내림을 통해 현재 자릿수를 직접 처리하는 것이 더 효율적입니다. 이 경우, 해당 자릿수만큼 돌을 추가로 사용하여 현재 층수를 0층으로 더 가까이 이동시킵니다.
while (storey > 0) {
...
// 내림
if (ones < 5 || (ones == 5 && tens < 5)) {
storey = storey / 10; // 상위 자릿수로 내림하여 이동
stone += ones; // 현재 일의 자릿수만큼 돌 개수를 추가
}
}
- 일의 자릿수가 5보다 작거나, 5이면서 십의 자릿수가 5 미만일 때, 내림을 선택합니다. 예를 들어,
12
층에서-2
로 직접 10층으로 이동하는 것이 더 효율적입니다. - 내림을 할 때는 현재 층수를 10으로 나누어 상위 자릿수로 이동하며, 현재 일의 자릿수만큼 돌 개수를 추가합니다. 이로써, 작은 수를 단순히 줄여나가면서 효율적으로 0층에 도달할 수 있습니다.
이 과정을 통해, 보다 작은 수를 직접 처리하는 방식으로 돌 사용을 최소화할 수 있습니다.
7. 최종 결과 반환
storey
가 0층에 다다를 때까지, 각 자릿수에서 올림과 내림을 반복하여 최적의 경로를 찾은 후, 그 과정에서 사용된 최소 마법의 돌 개수를 구합니다. 모든 계산이 끝난 후, 이 값을 최종적으로 반환합니다.
// 최소 마법의 돌 개수 반환
return stone;
이렇게 해서 주어진 storey
에서 0층으로 가기 위해 필요한 최소 마법의 돌 개수를 계산할 수 있습니다. 이 알고리즘은 각 자릿수에서의 최적 선택을 통해 전체적인 돌 사용을 최소화하는 방식으로, 효율적이고 빠른 방법으로 문제를 해결합니다.
Complexity
- ⏳ TC: O(log n)
- 💾 SC: O(1)
storey
를 10으로 나누며 각 자리를 계산하기 때문에, 시간 복잡도는 O(log n)입니다. 추가적인 메모리 사용 없이, 주어진 변수만으로 해결하므로 공간 복잡도는 O(1)입니다. 이 알고리즘은 각 자리에서 최적의 선택을 반복하여 전체적으로 최적의 결과를 도출합니다.
- Algorithm
- Greedy