본문 바로가기
알고리즘

[프로그래머스] 숫자 변환하기

by limew 2023. 12. 6.

https://school.programmers.co.kr/learn/courses/30/lessons/154538#

문제요점

  • 세 연산의 모든 조합
  • 숫자가 y초과하면 더이상 연산할 필요없다

 

첫번째 풀이 bfs (성공)

 

  • 최소 연산 횟수 => bfs 를 생각했다.
  • y가 x로 변환하는 방법
  • x미만의 결과가 나오는 연산은 고려할 필요없다
  • 최소연산 횟수 만에 찾기위해 num /3, num/2, num-n 순으로 찾았다.
function solution(x, y, n) {
    const queue = [{num: y, count: 0}];
    
    while(queue.length) {
        const {num, count} = queue.shift(); // shift는 O(n);
        // x가 되는 연산을 찾음
        if (num === x) {
            return count;
        }
        if (num % 3 === 0) {
            queue.push({num: num/3, count: count+1});
        }
        if (num % 2 === 0) {
            queue.push({num: num/2, count: count+1});
        }
        if (num-n >= x) {
            queue.push({num: num-n, count: count+1});
        }
    }
    return -1;
}

 

 

10번 테케가 상당히 오래걸렸다.

 


두번째 풀이 dp (성공)

시간을 줄이기 위해 dp로 수정해보았다.

dp[i] : i값에 도달하기 위한 최소 횟수

 

y+1 빈 배열을 생성하고 Inifity로 최소횟수를 init한다

dp[x] = 0으로 초기화하고 

순회하며 x 이후부터 y까지 dp배열을 채운다

dp[y] 값을 리턴한다.

 

function solution(x, y, n) {
    if (x === y) return 0; // 이미x,y의 값이 같을때
    const dp = Array.from({length: y+1}, () => Infinity);
    dp[x] = 0; 
    
    for (let i = x; i <= y; ++i) {
        if (dp[i+n]) {
            dp[i+n] = Math.min(dp[i+n], dp[i]+1);
        }
        if (dp[i*2]) {
            dp[i*2] = Math.min(dp[i*2], dp[i]+1);
        }
        if (dp[i*3]) {
            dp[i*3] = Math.min(dp[i*3], dp[i]+1);
        }
    }
    return dp[y] === Infinity ? -1 : dp[y];
}

 


🚀 세번째 풀이 (성공, 속도 향상)

x에서 y로 상향식 진행을 할 경우
3x, 2x, x+n 은 모두 정수가 되기 때문에
따져봐야 할 경우의 수가 많지만

 

y에서 x로 하향식 진행을 하면
y/3, y/2, y-n 은 적은 경우에만 정수가 되기 때문에 가지치기 속도가 향상 된다.

 

정수인지 확인하기

- Number.isInteger()
- 모듈러 연산 = 0

 

function solution(x, y, n) {
    const dp = Array.from({length: y+1}, () => Infinity);
    dp[y] = 0;
    
    for (let i = y; i >= x; --i) {
        if (i % 3 === 0) {
            const divideByThree = i/3;
            dp[divideByThree] = Math.min(dp[i]+1, dp[divideByThree]);
        }
        if (i % 2 === 0) {
            const divideByTwo = i/2;
            dp[divideByTwo] = Math.min(dp[i]+1, dp[divideByTwo]);
        }
        if (i - n >= x) {
            const subtractN = i-n;
            dp[subtractN] = Math.min(dp[i]+1, dp[subtractN]);
        }
    }
    return dp[x] === Infinity ? -1 : dp[x];
}

 

 

 

풀이

bfs

  • y를 x로 변환하는 세 연산의 조합 중 가장 빨리 x가 되는 경우의 연산횟수를 리턴한다.
  • 먼저 {num: y, count: 0}로 시작
  • num/3이 정수이면 queue에 {num: num/3, count: count+1}를 push한다
  • num/2이 정수이면 queue에 {num: num/2, count: count+1}를 push한다
  • num-n이 x보다 크거나 같으면 queue에 {num: num-n, count: count+1}를 push한다

dp

  • dp를 y+1 길이의 배열로 초기화한다. 각 배열의 값은 i가 되기까지 최소연산횟수이고 초기값은 Infinity다
  • dp[y] = 0이다 (y에서 연산을 시작한다.)
  • for문에서 i = y에서 i >= x까지 순회하며 가능한 경우를 dp에 갱신한다.
    • dp[i] = Math.min(dp[i], dp[i의 하향식 세가지 연산] + 1)

 

배운점

  • bfs를 사용하면 모든 경우를 비교할 필요없이 가장 먼저(최소) 만족하는 답을 찾을 수 있다.
  • 시간초과 => dp나 hash 사용
  • dp[i] = i값에 도달하기 위한 최소값
  • 하향식 진행 (y -> x)
  • 가장 빠른 탐색을 위해 큰 연산 먼저 수행

'알고리즘' 카테고리의 다른 글

[프로그래머스 LV2] 마법의 엘리베이터 JS  (0) 2023.12.07
비트마스킹  (0) 2023.12.06
[프로그래머스] 코딩테스트 공부  (0) 2023.11.25
우선순위 큐, 힙  (0) 2023.11.22
[카카오 2022] 등산코스 정하기  (0) 2023.11.18