안녕하세요

프로그래머스 JS 깊이/너비 우선 탐색(DFS/BFS) [4/7] 본문

프로그래머스

프로그래머스 JS 깊이/너비 우선 탐색(DFS/BFS) [4/7]

sakuraop 2023. 9. 12. 00:56

단어 변환(lv3) 문제 보기 (25)

문제 풀이

고려해야할 문제의 조건은 세가지다.

  • 한번에 한개의 알파벳 변경 가능
  • words에 존재하는 단어로만 변환 가능
  • 중복되는 단어 없음

목표는

words 배열의 길이만큼 변환 시도를 했을 때, 변환할 수 없다면 0을 반환하고,

변환이 가능하다면 최소 변환 횟수를 구하는 것이다.

 

 
solution("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cig"]);
 

 

1. words 배열의 길이만큼 변환 시도를 한다.

    words.forEach((word, index) => {
    });

 

2.  단어 변환이 가능한지 확인한다.

하나의 알파벳만이 다르면 변환이 가능하기 때문에 몇 개의 알파벳이 일치하는지 확인하면 된다.

      let sameCount = 0;
      for (let i = 0; i < wordLength; i++) {
        if (baseWord[i] === compareWord[i]) {
          sameCount++;
        }
      }

 

3.

단어 변환이 가능하면 해당 단어로 변환한다, 변환된 단어는 방문처리 한다, count +1을 한다

변경 사항을 dfs에 전달한다.

변환 전 상태로 되돌린다.

      if (sameCount === wordLength - 1) {
        compareWord = word;
        words[index] = "";
        dfs(words, compareWord, count + 1);
        compareWord = begin;
        words[index] = word;
      }

 

전체 코드

 
function solution(begin, target, words) {
  const wordLength = words[0].length;
  let result = words.length + 1;

  const dfs = (words, compareWord, count) => {
    const begin = compareWord;

    if (compareWord === target) {
      result = Math.min(count, result);
      return;
    }

    words.forEach((word, index) => {
      let sameCount = 0;
      for (let i = 0; i < wordLength; i++) {
        if (baseWord[i] === compareWord[i]) {
          sameCount++;
        }
      }

      if (sameCount === wordLength - 1) {
        compareWord = word;
        words[index] = "";
        dfs(words, compareWord, count + 1);
        compareWord = begin;
        words[index] = word;
      }
    });
  };

  dfs(words, begin, 0);

  return result === words.length + 1 ? 0 : result;
}
solution("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cig"]);
 

 

타겟 넘버(lv2) 문제 보기 (5)

문제 풀이

numbers의 각 자리에 대하여 각각 한번씩 +, -을 한 결과를 만들면 된다.

0 으로 시작하여, numbers[i] 번째까지 확인하도록 하려면 어떻게 해야할지 생각하면 된다.

 

1. numbers의 길이만큼 더하거나 뺐다면 재귀를 더이상 실행하지 않도록 한다.

2. i회 만큼 더하거나 뺀 수의 결과가 target 과 같다면 count를 ++ 한다.

 

 
    if (i === numbersLength) {
      if (number === target) {
        count++;
      }
      return;
    }
 

 

3. 0에서 시작하여 0 (+ or -) numbers[i] 를 한 다음 numbers[i]를 더하거나 뺄 수 있도록 i +1 을 해준다.

 

 
    dfs(number + numbers[i], i + 1);
    dfs(number - numbers[i], i + 1);
 
 
  dfs(0, 0);
 
function solution(numbers, target) {
  const numbersLength = numbers.length;

  let count = 0;

  const dfs = (number, i) => {
    if (i === numbersLength) {
      if (number === target) {
        count++;
      }
      return;
    }

    dfs(number + numbers[i], i + 1);
    dfs(number - numbers[i], i + 1);
  };

  dfs(0, 0);

  return count;
}

게임 맵 최단거리(lv2) 문제 보기 (10)

문제 해결

다음으로 이동 가능한 좌표는 상, 하, 좌, 우 넷 중에 하나이다.

이동 가능한 좌표가 1이면 이동을 할 수 있다.

=> 게임 보드 안에서 상하좌우 중에서 좌표가 1인 경우에 이동한다.

 

const canMove = (xMove, yMove, n, m, maps) => {
  return xMove >= 0 && yMove >= 0 && xMove < n && yMove < m && maps[xMove][yMove] === 1;
};

 

방문한 좌표는 1에서 0으로 바꾸어 준다.

 

문제를 해결하기 위해서는 queue를 이용하여야 한다

stack을 이용하였을 때와 queue를 이용하였을 때 차이를 설명하자면,

 

1  : 다음으로 이동가능한 좌표

[
  [ 0, 0, 1, 1, 1 ],
  [ 0, 0, 1, 0, 1 ],
  [ 0, 0, 1, 1, 1 ],
  [ 0, 0, 1, 0, 1 ],
  [ 0, 0, 0, 0, 1 ]
]
[
  [ 0, 0, 1, 1, 1 ],
  [ 0, 0, 1, 0, 1 ],
  [ 0, 0, 1, 1, 1 ],
  [ 0, 0, 0, 0, 1 ],
  [ 0, 0, 0, 0, 1 ]
]

[
  [ 0, 0, 1, 1, 1 ],
  [ 0, 0, 1, 0, 1 ],
  [ 0, 0, 0, 1, 1 ],
  [ 0, 0, 0, 0, 1 ],
  [ 0, 0, 0, 0, 1 ]
]

이 분기에서 dfs라면 위쪽 경로를 먼저 선택했을 때, p가 먼저 최종 목표에 도달한다.

오른쪽 경로가 출발했을 때는 이미 방문처리된 (2,4)로 인해 이동할 수 없게 되고,

결국 최단거리가 아니게 된다.

 

[
  [ 0, 0, 1, 1, 1 ],
  [ 0, 0, 0, 0, 1 ],
  [ 0, 0, 0, 0, 1 ],
  [ 0, 0, 0, 0, 1 ],
  [ 0, 0, 0, 0, 1 ]
]

하지만 bfs라면 위쪽 경로와 오른쪽 경로를 함께 탐색하여

오른쪽을 선택한 경로가 위쪽으로 탐색하려는 경로보다 우선하여 (2,4)를 지나가게 되고,

최종 목적지에 도달하는 것은 오른쪽을 선택한 경로가 되어 최단거리를 구할 수 있게 된다. 

 

const canMove = (xMove, yMove, n, m, maps) => {
  return xMove >= 0 && yMove >= 0 && xMove < n && yMove < m && maps[xMove][yMove] === 1;
};

function solution(maps) {
  const [n, m] = [maps.length, maps[0].length]; // 보드 크기
  const p = [0, 0, 0]; // 플레이어의 x좌표, y좌표, 이동 횟수

  const moveQueue = [p]; // 초기 플레이어의 좌표 설정

  const dirs = [
    [-1, 0], // 상
    [1, 0], // 하
    [0, -1], // 좌
    [0, 1], // 우
  ];

  // 이동이 불가능할 때까지 시행합니다.
  while (moveStack.length) {
    const [x, y, count] = moveStack.shift(); // queue를 사용하지 않으면 항상 최단거리가 아니게 된다.

    // p의 좌표가 목적지에 위치해 있다면 이동 횟수를 반환하여 게임을 종료합니다.
    if (x === n - 1 && y === m - 1) {
      return count + 1;
    }

    for (let dir of dirs) {
      const [dx, dy] = dir;
      const xMove = x + dx;
      const yMove = y + dy;

      // 다음으로 이동가능한 좌표가 존재할 경우
      if (canMove(xMove, yMove, n, m, maps)) {
        // 해당 좌표로 이동
        maps[xMove][yMove] = 0; // 방문 표시 남기기
        moveQueue.push([xMove, yMove, count + 1]);
      }
    }
  }

  // 최종 목적지에 도달하지 못했다면 -1을 반환합니다.
  return -1;
}

네트워크(lv3) 문제 보기 (60)

문제 해결

1. 각각의 컴퓨터에 연결된 네트워크를 기록한다.

{
  '0': [ 1 ], 
  '1': [ 0, 2 ], 
  '2': [ 1 ] 
}

 

2. 처음에는 모든 네트워크가 끊어져 있다고 가정한다.

let count = n;

 

3. n번째 컴퓨터와 연결되어 있는 네트워크 수 만큼 count-- 를 해주면 총 네트워크 수를 확인할 수 있다.

확인하는 컴퓨터와, 다음으로 연결할 컴퓨터의 방문처리를 신경써주면 된다.

 

아래 설명을 보면 쉽게 이해할 수 있을 것이다.

 

ex1)

[

  [1,1,0],

  [1,1,0],

  [0,0,1]

]

1번째가 2번째가 연결

3번째는 연결 X

 

let count = 3;

 

1번째 컴퓨터 확인 => 1번째 컴퓨터 방문처리 => 2번째와 연결되어 있음 => 2번째 방문처리 => count = 2

2번째 컴퓨터 확인 => 1번째와 연결되어 있으나 이미 방문처리 되어 있음 => 더 이상 연결된 컴퓨터 없음 count = 2

3번째 컴퓨터 확인 => 연결된 컴퓨터가 없음 => count  = 2

 

총 네트워크 수는 2개가 된다.


ex2)

[

  [1,1,0,0],

  [1,1,0,0],

  [0,0,1,1],

  [0,0,1,1],

]

1번째와 2번째가 연결

3번째와 4번째가 연결

 

let count = 4;

 

1번째 확인 => 1번째 방문처리 => 2번째와 연결되어 있음 => 2번째 방문처리 => count = 3

2번째 확인 => 1번째와 연결되어 있으나 이미 방문처리 되어 있음 => 더 이상 연결 없음 count = 3

3번째 확인 => 3번째 방문처리 => 4번째와 연결되어 있음 => 4번째 방문처리 => count  = 2

4번째 확인 => 3번째와 연결되어 있으나 이미 방문처리 되어 있음 => 더 이상 연결 없음 count = 2

 

const solution = (n, computers) => {
  const obj = {}; // 각각의 컴퓨터와 연결된 네트워크를 기록
  const visited = Array.from({ length: n }).fill(0);

  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
      // 자기 자신은 항상 1이므로 제외
      if (i === j) continue;

      // i번째 컴퓨터와 연결된 컴퓨터의 index기록
      if (computers[i][j]) {
        obj[i] = obj[i] ? [...obj[i], j] : [j];
      }
    }
  }

  let count = n; // 처음엔 모든 네트워크가 끊겨있다고 가정하고,

  // 각각의 컴퓨터를 확인
  for (let i in obj) {
    const queue = [...obj[i]];
    visited[i] = 1; // i번째 네트워크 방문처리

    // 연결된 네트워크가 있을 때
    while (queue.length) {
      const next = queue.shift();

      // 방문하지 않은 네트워크라면
      if (!visited[next]) {
        visited[next] = 1; // 다음번 네트워크 방문처리
        queue.push(...obj[next]); // 해당 컴퓨터와 연결된 네트워크들을 추가
        count--; // 네트워크 연결
      }
    }
  }

  return count;
};