코딩 테스트/프로그래머스

[프로그래머스] 게임 맵 최단거리

2023. 4. 29. 10:47
목차
  1. 📙 1. 문제
  2. 문제 설명
  3. 제한 사항
  4. 풀이 1 : DFS (시간초과)
  5.  풀이 2 : BFS (성공)
  6. 🤔 2. 느낀 점
  7. 🤩 3. 한 번 더 짚고 갈 점
  8. BFS와 DBS 사용하는 경우

📙 1. 문제

Link : https://school.programmers.co.kr/learn/courses/30/lessons/1844

문제 설명

문제 설명
ROR 게임은 두 팀으로 나누어서 진행하며, 상대 팀 진영을 먼저 파괴하면 이기는 게임입니다. 따라서, 각 팀은 상대 팀 진영에 최대한 빨리 도착하는 것이 유리합니다.

지금부터 당신은 한 팀의 팀원이 되어 게임을 진행하려고 합니다. 다음은 5 x 5 크기의 맵에, 당신의 캐릭터가 (행: 1, 열: 1) 위치에 있고, 상대 팀 진영은 (행: 5, 열: 5) 위치에 있는 경우의 예시입니다.



위 그림에서 검은색 부분은 벽으로 막혀있어 갈 수 없는 길이며, 흰색 부분은 갈 수 있는 길입니다. 캐릭터가 움직일 때는 동, 서, 남, 북 방향으로 한 칸씩 이동하며, 게임 맵을 벗어난 길은 갈 수 없습니다.
아래 예시는 캐릭터가 상대 팀 진영으로 가는 두 가지 방법을 나타내고 있습니다.

첫 번째 방법은 11개의 칸을 지나서 상대 팀 진영에 도착했습니다.



두 번째 방법은 15개의 칸을 지나서 상대팀 진영에 도착했습니다.



위 예시에서는 첫 번째 방법보다 더 빠르게 상대팀 진영에 도착하는 방법은 없으므로, 이 방법이 상대 팀 진영으로 가는 가장 빠른 방법입니다.

만약, 상대 팀이 자신의 팀 진영 주위에 벽을 세워두었다면 상대 팀 진영에 도착하지 못할 수도 있습니다. 예를 들어, 다음과 같은 경우에 당신의 캐릭터는 상대 팀 진영에 도착할 수 없습니다.



게임 맵의 상태 maps가 매개변수로 주어질 때, 캐릭터가 상대 팀 진영에 도착하기 위해서 지나가야 하는 칸의 개수의 최솟값을 return 하도록 solution 함수를 완성해주세요. 단, 상대 팀 진영에 도착할 수 없을 때는 -1을 return 해주세요.

제한 사항

  • maps는 n x m 크기의 게임 맵의 상태가 들어있는 2차원 배열로, n과 m은 각각 1 이상 100 이하의 자연수입니다.
    • n과 m은 서로 같을 수도, 다를 수도 있지만, n과 m이 모두 1인 경우는 입력으로 주어지지 않습니다.
  • maps는 0과 1로만 이루어져 있으며, 0은 벽이 있는 자리, 1은 벽이 없는 자리를 나타냅니다.
  • 처음에 캐릭터는 게임 맵의 좌측 상단인 (1, 1) 위치에 있으며, 상대방 진영은 게임 맵의 우측 하단인 (n, m) 위치에 있습니다.

 

풀이 1 : DFS (시간초과)

function solution(maps) {
    let answer = Infinity;
    const [ySize,xSize] = [maps.length,maps[0].length]
    
    const direction = [
        [0,1],
        [1,0],
        [0,-1],
        [-1,0],
    ];
    const dfs = (y,x,cnt) => {
        if(y===ySize-1 && x===xSize-1){
            answer = Math.min(answer,cnt)
            return;
        }
        for(let i=0;i<4;i++){
            let [goY,goX]=[y+direction[i][0],x+direction[i][1]]
            if(goY<0 || goY>=ySize || goX<0 || goX>=xSize || !maps[goY][goX]) continue;
            maps[goY][goX]=0
            dfs(goY,goX,cnt+1);
            maps[goY][goX]=1
        }
    }
    maps[0][0]=0;
    dfs(0,0,0);
    return answer === Infinity?-1:answer+1
}

 

정확성 테스트는 모두 통과하였다. 하지만 DFS는, 다른 경우에서 먼저 목적지에 도착해도 모든 경우의 수를 탐색하기 때문에 시간초과로 인해 효율성 테스트를 통과하지 못했다.

 

 풀이 2 : BFS (성공)

function solution(maps) {
    let answer = Infinity;
    const [ySize,xSize] = [maps.length,maps[0].length]
    
    const direction = [
        [0,1],
        [1,0],
        [0,-1],
        [-1,0],
    ];
    const queue = [];
    queue.push([0,0,0]);
    maps[0][0] = 0;
    while(queue.length>0){
        let [y,x,cnt] = queue.shift();
        if(y===ySize-1 && x===xSize-1) {
            answer=Math.min(cnt,answer)
            break;
        }
        for(let i=0;i<4;i++){
            let [goY,goX]=[y+direction[i][0],x+direction[i][1]]
            if(goY<0 || goY>=ySize || goX<0 || goX>=xSize || !maps[goY][goX]) continue;
            maps[goY][goX]=0
            queue.push([goY,goX,cnt+1])
        }
    }
    return maps[ySize-1][xSize-1]===1?-1:answer+1
}
  • queue에 [y,x,cnt]를 push 하며 탐색하였다.
  • 방문할 노드는 queue에 push 하고, 방문하였다면 shift 하는 방식이다.
  • 방문하려는 노드가 maps범위를 벗어나거나, 값이 0이라면(즉, 벽이거나 이미 방문한 노드) continue로 queue에 push 하지 않는다.
  • queue에 push 하며 해당 노드는 0으로 값을 변경하여 지나왔음을 표시한다. -> visited배열을 따로 관리할 필요가 없다.

🤔 2. 느낀 점

과거에는 'maps.length'를 직접 사용하여 하드코딩했었지만, 이번에는 ySize와 xSize와 같이 미리 변수 화하여 사용하였다. 이렇게 하니 코드가 더 직관적이고 간결해지며, 실수가 줄어든 것을 느낄 수 있었다. ySize와 xSize 변수를 알맞게 설정하면, "여기서 문제가 있을까?" 걱정을 하지 않아도 되어 자연스럽게 실수가 줄어들고 시간도 절약된다.
비슷한 BFS 문제를 약 1년 전에도 많이 풀어보았다. 그때와 비교해 이번에 작성한 BFS 코드가 훨씬 깔끔해진 것을 확인할 수 있다. 다양한 알고리즘 문제를 풀어보며 실력이 분명히 향상되어 뿌듯한 기분이 든다.

 

🤩 3. 한 번 더 짚고 갈 점

DFS와 BFS는 같은 탐색 방법이 절대 아니다. 고로 소요되는 시간 또한 다르다. 그러한 점을 고려하여 앞으로는 문제 특성에 맞게 DFS를 사용할지, BFS를 사용할 지 결정하도록 하자.

BFS와 DBS 사용하는 경우

참고

1) 그래프의 모든 정점을 방문해야 하는 문제 : DFS, BFS 뭐든 상관없다. 편한 걸 쓰면 된다.
2) 경로의 특징을 저장하는 문제 : 예시로 각 정점에 숫자가 있고 a부터 b까지 경로를 구하는데 경로에 같은 숫자가 있으면 안 된다는 문제. 각각의 경로마다 그 특징을 저장해두어야 하는 문제는 DFS를 사용한다.
3) 최단거리를 구하는 문제 : 미로 찾기 등 최단거리를 구하는 경우 BFS가 좋다. DFS로 경로를 탐색하면 처음 발견하는 해답이 최단거리가 아닐 수도 있지만 BFS는 현재노드에서 가까운 곳부터 찾기 때문에 경로를 탐색 시 먼저 찾아지는 해답이 곧 최단거리다.
4) 검색대상그래프가 많이 클 때 DFS가 좋다고 한다.
5) 검색대상의 규모가 별로 크지 않고 검색시작지점으로부터 원하는 대상이 별로 멀지 않으면 BFS.

 

위 문제의 경우 3번에 해당하여, BFS가 적합하다.

 
저작자표시 (새창열림)

'코딩 테스트 > 프로그래머스' 카테고리의 다른 글

[프로그래머스] 후보키  (0) 2023.05.12
[프로그래머스] 문자열 압축  (0) 2023.05.11
[프로그래머스] 순위 검색  (0) 2023.05.07
[프로그래머스] 양궁대회  (1) 2023.04.30
[프로그래머스] 이모티콘 할인행사  (0) 2023.04.28
[프로그래머스] 택배 배달과 수거하기  (0) 2023.04.27
[4월 2일] 시소 짝꿍  (0) 2023.04.02
[3월 31일] 뒤에 있는 큰 수 찾기 | 숫자 변환하기  (0) 2023.03.31
  1. 📙 1. 문제
  2. 문제 설명
  3. 제한 사항
  4. 풀이 1 : DFS (시간초과)
  5.  풀이 2 : BFS (성공)
  6. 🤔 2. 느낀 점
  7. 🤩 3. 한 번 더 짚고 갈 점
  8. BFS와 DBS 사용하는 경우
'코딩 테스트/프로그래머스' 카테고리의 다른 글
  • [프로그래머스] 순위 검색
  • [프로그래머스] 양궁대회
  • [프로그래머스] 이모티콘 할인행사
  • [프로그래머스] 택배 배달과 수거하기
피터s
피터s
1년차 프론트엔드 개발자입니다 😣 아직 열심히 배우는 중이에요! 리액트를 하고있어요 :) - gueit214@naver.com - https://github.com/gueit214
피터s
피터의 성장기록
피터s
전체
오늘
어제
  • 분류 전체보기 (200)
    • 코딩 테스트 (25)
      • 프로그래머스 (16)
      • LeetCode (8)
      • 백준 (1)
    • 개발 독서 일지 (1)
    • 기업 분석 (4)
    • 개발 일지 (19)
      • 최신기술 도전기 (1)
      • 에러 처리 (5)
      • 개발 일지 (12)
    • 개발 일상 (36)
      • 개발 회고 (22)
      • 개발 이야기 (12)
      • 개발 서적 (1)
    • 취업 관련 지식 (11)
    • 알고리즘 (17)
    • WebProgramming (84)
      • WebProgramming (8)
      • HTML (5)
      • CSS (8)
      • JS (21)
      • React (40)

블로그 메뉴

  • About
  • 2022년 개발 성장기
  • 앞으로의 계획
  • github
  • 일상 blog

공지사항

인기 글

태그

  • 카카오
  • BFS
  • 1일 1커밋 후기
  • Union-find
  • 함수
  • dfs
  • 개발 일상
  • 해커톤
  • 구름
  • 카카오 채용연계형 인턴십
  • 누적합
  • KAKAO BLIND
  • 스터디 후기
  • 개발 is life
  • lv3
  • Kakao Tech Internship
  • 구름톤
  • LV2
  • 1년 회고
  • 반복문
  • Retry
  • 개발 회고

최근 댓글

최근 글

hELLO · Designed By 정상우.
피터s
[프로그래머스] 게임 맵 최단거리
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.