Development/자료구조 & 알고리즘

[알고리즘] 프로그래머스 - 숫자 변환하기 (JS)

heondeam 2023. 8. 30. 02:34

 

✅ 문제

자연수 x를 y로 변환하려고 합니다. 사용할 수 있는 연산은 다음과 같습니다.

  • x에 n을 더합니다
  • x에 2를 곱합니다.
  • x에 3을 곱합니다.

자연수 x, y, n이 매개변수로 주어질 때, x를 y로 변환하기 위해 필요한 최소 연산 횟수를 return하도록 solution 함수를 완성해주세요. 이때 x를 y로 만들 수 없다면 -1을 return 해주세요.

 

✅ 풀이

첫 번째 접근방식 (DFS)

모든 경우의 수를 DFS로 탐색하면서 nx === y 일 때 answer에 담아준 뒤 최솟값을 출력해주었다. 결과는 반만 맞았다. 런타임 에러는 왜 발생하는지 정확하게는 모르겠지만.. 추측건데 재귀로 인한 스택 오버 플로우 문제인 것 같다.

  • 점수 50 / 100
  • 테스트 케이스
    • 5, 9, 10, 11, 14 (런타임 에러)
    • 15, 16 (시간 초과)

 

function solution(x, y, n) {
  let answer = [];
  
  const dfs = (nx, depth = 0) => {
    if (nx >= y) {
      if (nx === y) {
        answer.push(depth);
      }
      
      return;
    }
    
    dfs(nx * 3, depth + 1);
    dfs(nx * 2, depth + 1);
    dfs(nx + n, depth + 1);
  }
  
  dfs(x);
  
  return answer.length > 0 ? Math.min(...answer) : -1;
}

 

두 번째 접근 방식 (BFS)

더 나은 시간복잡도를 위해 BFS를 사용하여 탐색이 가능한 경우로만 탐색해주는 방식으로 풀이했다. 런타임 에러는 발생하지 않았고, 5개의 테스트 케이스만 시간 초과로 실패했다. 여기서 시간을 더 줄일 수 있는 방법이 뭐가 있을까 1시간 넘게 생각하다가 결국 다른 사람들의 풀이에서 도움을 얻었다.

  • 점수 68.8 / 100
  • 테스트 케이스
    • 5, 7, 9, 10, 11 (시간 초과)

 

function solution(x, y, n) {
  const bfs = (sx) => {
    const queue = [[sx, 0]];
    
    while (queue.length) {
      const [sx, sd] = queue.shift();
      
      if (sx === y) {
        return [sx, sd];        
      }
      
      if (sx * 3 <= y) {
        queue.push([sx * 3, sd + 1]);
      }
      
      if (sx * 2 <= y) {
        queue.push([sx * 2, sd + 1]);
      }
      
      if (sx + n <= y) {
        queue.push([sx + n, sd + 1]);
      }
    }
    
  }
  
  const answer = bfs(x);
  
  return answer ? answer[1] : -1;
}

세 번째 접근 방식 (BFS + y를 기준으로 역순회) (통과)

문제를 처음 풀이했을 때 y를 기준으로 각 1,2,3 각 조건들을 반대로 수행하며 x를 만들어나갔었는데, 그 방식을 BFS와 함께 사용하면 충분히 시간을 단축시킬 수 있다.

 

function solution(x, y, n) {
  const bfs = (sy) => {
    const queue = [[sy, 0]];
    
    while (queue.length) {
      const [ny, nd] = queue.shift();
      
      if (ny === x) {
        return [ny, nd];        
      }
      
      if (ny % 3 === 0) {
        queue.push([ny / 3, nd + 1]);
      }
      
      if (ny % 2 === 0) {
        queue.push([ny / 2, nd + 1]);
      }
      
      if (ny - n >= x) {
        queue.push([ny - n, nd + 1]);
      }
    }
    
  }
  
  const answer = bfs(y);
  
  return answer ? answer[1] : -1;
}

 

시간복잡도를 개선하는 것이 쉽지 않다는 것을 다시 한 번 느꼈다. 한 번에 여러 방향을 보기가 힘들지만, 연습하는 것 밖에 답이 없을 것 같다. 정진하자.