https://school.programmers.co.kr/learn/courses/30/lessons/148653?language=java
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
시나리오
현재 층수의 일의 자릿수를 조사, 5보다 크면 (10-일의 자릿수)만큼 덧셈하고, 작으면 일의 자릿수만큼 뺄셈하여 끝자리를 0으로 맞춘다. 한번에 이동할 수 있는 층수는 절댓값이 \(10^c)\인 정수이기 때문이다.
일의 자릿수를 덧셈/뺄셈하여 끝자리를 맞췄다면 다음으로 십의 자릿수를 조사, 같은 방법으로 해당 자릿수를 0으로 맞춘다. 이 과정은 가장 앞자리까지 반복하며, 덧셈/뺄셈한 값만큼 누적하여 return한다.
다만 각 자릿수가 5라면, 현재 자릿수의 앞자릿수를 조사한다. 이 때 앞자릿수가 5이상이면 덧셈하는 것이 유리하고, 5미만이라면 뺄셈하는 것이 유리하다.
이 시나리오는 정답 코드들의 시나리오를 가져온 것이며
첫번째 시도는 무작정 BFS로 풀었다.
첫 시도
import java.util.*;
class Solution {
public int solution(int storey) {
int answer = 0;
answer = bfs(storey);
return answer;
}
//bfs. +_1 ~ +-100까지 적용한 모든 경우 enqueue
private int bfs(int s){
//chk를 통해 이미 간 곳은 가지 않도록. chk배열을 int로 해서 특정 층까지 가는 데 소요된 마법의 돌 개수 카운팅
boolean[] chk = new boolean[100000001];
chk[s]=true;
Queue<int[]> q = new LinkedList<>();
int[] init = {s,0};
q.offer(init);
while(!q.isEmpty()){
int[] polled = q.poll();
System.out.println(Arrays.toString(polled));
if(polled[0]==0){
return polled[1];
}
//절댓값이 10^c인 수를 +/- 했을 때 조건 비교 및 enqueue
for(int i=0; i<=8; i++){
int res = polled[0]-(int)(Math.pow(10,i));
if(res>=0 && !chk[res]){
chk[res]=true;
int[] e = {res, polled[1]+1};
q.offer(e);
}
res = polled[0]+(int)(Math.pow(10,i));
if(res<=100000000 && !chk[res]){
chk[res]=true;
int[] e = {res, polled[1]+1};
q.offer(e);
}
}
}
return -1;
}
}
//가지치기
/*
???
*/
//마법의 돌의 최소 값
각 자릿수를 비교하여 정답을 도출한게 아니라 1부터 1억까지 더하거나 빼는 모든 경우의 수를 BFS로 구했기 때문에 그 가짓수가 너무 많아졌고, 예시 테스트케이스는 통과했으나 대부분의 테스트케이스에서 시간초과가 발생하였다.
내가 풀 수 없음을 직감하고 정답 코드들을 참고했고 그들의 시나리오를 참고한 뒤 혼자 푼 코드이다.
두번째 시도
class Solution {
private int answer=0;
public int solution(int storey) {
while(storey!=0){
storey = calcStorey(storey);
}
return answer;
}
//자릿수 계산 하여 갱신된 층수 반환
private int calcStorey(int storey){
int div = storey%10;
if(div<5){
storey-=div;
calcAnswer(div);
}else if(div>5){
storey+=10-div;
calcAnswer(10-div);
}else{
if(hasToAdd(storey)){
storey+=div;
}else{
storey-=div;
}
calcAnswer(div);
}
if(storey/10<=0){
return 0;
}
return storey/10;
}
//answer누적합
private void calcAnswer(int num){
answer+=num;
}
//현재 자릿수가 5일 때 앞자리를 보고 판단하는 함수
private boolean hasToAdd(int storey){
if(storey<10){
return false;
}
if(storey/10%10<5){
return false;
}
return true;
}
}
//자릿수가 5일 때는 앞자리 수를 보고 판단. 앞자리 수가 5이상이면 현재 자릿수를 더하고 5보다 작으면 현재 자릿수를 빼는 게 더 적은 층을 이동할 수 있다.
//입력받은 현재 층수에 대해 일의 자릿수를 조사, 5보다 크면 10-일의 자릿수 만큼 더하고, 작으면 뺄셈하여 10의 약수로 맞춘다. 현재 층수의 일의 자릿수부터 조사, 층수를 반올림하고 이동한 칸만큼 answer+=, 다음 자릿수를 조사하기 위해 storey//10. 반복
//TC
// +4: 20 -> +2 : 0
//4+2=6
// +4: 2550 -> +5: 2600 -> +4: 3000 -> +3: 0
// +4: 2550 -> +5 :2500 -> +5: 2000 -> +2: 0
//4+5+4+3 = 16
BFS로도 가지치기를 잘한다면 풀 수 있겠지만, 각 자릿수를 활용하는 아이디어를 활용했을 때 가장 효율적인 문제라고 생각한다.
문제를 풀다보면 이렇게 최단거리?를 구해야하는 경우가 종종 있는데 자릿수를 반올림하는 아이디어로도 풀 수 있음을 배울 수 있었다.
'Algorithm > 그리디' 카테고리의 다른 글
| [Java] 프로그래머스 - 요격 시스템 (0) | 2023.12.12 |
|---|---|
| [Java] 프로그래머스 - 호텔 대실 (0) | 2023.05.03 |
| [Java] 백준 2217 - 로프 (0) | 2023.04.08 |
| 프로그래머스 Lv.1 - 명예의 전당 (1) 파이썬 풀이 (0) | 2022.12.24 |
| 백준 1931-회의실 배정 (파이썬) (0) | 2022.10.04 |