Skip to content

Conversation

@uyeon0
Copy link
Collaborator

@uyeon0 uyeon0 commented Oct 22, 2025

User description

오늘도 멋져요 👍✨


PR Type

Enhancement


Description

  • 프로그래머스 Level 3 '미로 탈출 명령어' 문제 해결 코드 추가

  • DFS를 활용한 최적화된 경로 탐색 구현

  • 남은 거리와 최단 거리를 이용한 시간 복잡도 개선


@uyeon0 uyeon0 added the programmers Programmers 문제 풀이 label Oct 22, 2025
@github-actions
Copy link

PR Reviewer Guide 🔍

🧪 PR contains tests
⚡ Recommended focus areas for review

성능 최적화

DFS 탐색 중 불필요한 경로를 더 일찍 가지치기할 수 있습니다. 현재 위치에서 목적지까지 사전순으로 가장 빠른 경로를 미리 계산하여 남은 이동 횟수로 도달 불가능한 경우를 즉시 제외할 수 있습니다.

function dfs(pos, path) {
  if (answer !== "impossible") {
    return;
  }

  // 도착한 경우
  if (pos[0] === E[0] && pos[1] === E[1]) {
    if (path.length === k) {
      // 도착하기까지의 경로가 k와 같다면 이것이 답!
      answer = path;
      return;
    }
  }

  // 남은 거리를 이용해서 시간 단축하기 (몰랐어;)
  const remaining = k - path.length; // k까지 남은 거리
  const minDist = Math.abs(pos[0] - E[0]) + Math.abs(pos[1] - E[1]);
  // case 1. 현 위치부터 목적지까지 최단 거리보다 남은 거리가 더 작다면 목적지까지 도달할 수 없다.
  if (minDist > remaining) {
    return;
  }
  // case 2. 남은 거리가 양수일 때 그 남은 거리가 짝수가 아니면 답이 아니다. (왔다갔다로 남은 거리를 소비할 수 없음)
  // 도착 지점에 도달했을 때 "k보다 일찍 도착했다!" → "남은 거리가 짝수가 아니군" 을 검사하기엔 늦다 (시간 초과 발생함)
  // 도착 지점까지의 최단거리보다 남은 거리가 더 길기 때문에, 잉여 거리를 예측하고, 그 잉여 거리가 짝수인지를 확인한다.
  // 좀 더 가지를 칠 수 있다. (이걸 어떻게 생각해내는것임;)
  if ((remaining - minDist) % 2 !== 0) {
    return;
  }

  for (const [dx, dy, dirName] of dir) {
    const nx = pos[0] + dx;
    const ny = pos[1] + dy;

    if (nx >= 1 && nx <= n && ny >= 1 && ny <= m) {
      dfs([nx, ny], path + dirName);
      if (answer !== "impossible") {
        return;
      }
    }
  }
}
메모리 사용량

재귀적 DFS 호출로 인한 콜 스택 메모리 사용이 큽니다. 반복문 기반의 스택을 사용하거나 BFS로 전환하여 메모리 사용량을 개선할 수 있습니다.

function dfs(pos, path) {
  if (answer !== "impossible") {
    return;
  }

  // 도착한 경우
  if (pos[0] === E[0] && pos[1] === E[1]) {
    if (path.length === k) {
      // 도착하기까지의 경로가 k와 같다면 이것이 답!
      answer = path;
      return;
    }
  }

  // 남은 거리를 이용해서 시간 단축하기 (몰랐어;)
  const remaining = k - path.length; // k까지 남은 거리
  const minDist = Math.abs(pos[0] - E[0]) + Math.abs(pos[1] - E[1]);
  // case 1. 현 위치부터 목적지까지 최단 거리보다 남은 거리가 더 작다면 목적지까지 도달할 수 없다.
  if (minDist > remaining) {
    return;
  }
  // case 2. 남은 거리가 양수일 때 그 남은 거리가 짝수가 아니면 답이 아니다. (왔다갔다로 남은 거리를 소비할 수 없음)
  // 도착 지점에 도달했을 때 "k보다 일찍 도착했다!" → "남은 거리가 짝수가 아니군" 을 검사하기엔 늦다 (시간 초과 발생함)
  // 도착 지점까지의 최단거리보다 남은 거리가 더 길기 때문에, 잉여 거리를 예측하고, 그 잉여 거리가 짝수인지를 확인한다.
  // 좀 더 가지를 칠 수 있다. (이걸 어떻게 생각해내는것임;)
  if ((remaining - minDist) % 2 !== 0) {
    return;
  }

  for (const [dx, dy, dirName] of dir) {
    const nx = pos[0] + dx;
    const ny = pos[1] + dy;

    if (nx >= 1 && nx <= n && ny >= 1 && ny <= m) {
      dfs([nx, ny], path + dirName);
      if (answer !== "impossible") {
        return;
      }
    }
  }
}
엣지 케이스

격자 크기가 최대일 때(50x50)와 k가 최대값(2500)일 때의 성능 및 메모리 사용량을 고려해야 합니다. 또한 시작점과 도착점이 같은 경우의 처리가 필요합니다.

function solution(n, m, x, y, r, c, k) {
  let answer = "impossible";
  const S = [x, y];
  const E = [r, c];
  const dir = [
    [1, 0, "d"],
    [0, -1, "l"],
    [0, 1, "r"],
    [-1, 0, "u"],
  ];

  function dfs(pos, path) {
    if (answer !== "impossible") {
      return;
    }

    // 도착한 경우
    if (pos[0] === E[0] && pos[1] === E[1]) {
      if (path.length === k) {
        // 도착하기까지의 경로가 k와 같다면 이것이 답!
        answer = path;
        return;
      }
    }

    // 남은 거리를 이용해서 시간 단축하기 (몰랐어;)
    const remaining = k - path.length; // k까지 남은 거리
    const minDist = Math.abs(pos[0] - E[0]) + Math.abs(pos[1] - E[1]);
    // case 1. 현 위치부터 목적지까지 최단 거리보다 남은 거리가 더 작다면 목적지까지 도달할 수 없다.
    if (minDist > remaining) {
      return;
    }
    // case 2. 남은 거리가 양수일 때 그 남은 거리가 짝수가 아니면 답이 아니다. (왔다갔다로 남은 거리를 소비할 수 없음)
    // 도착 지점에 도달했을 때 "k보다 일찍 도착했다!" → "남은 거리가 짝수가 아니군" 을 검사하기엔 늦다 (시간 초과 발생함)
    // 도착 지점까지의 최단거리보다 남은 거리가 더 길기 때문에, 잉여 거리를 예측하고, 그 잉여 거리가 짝수인지를 확인한다.
    // 좀 더 가지를 칠 수 있다. (이걸 어떻게 생각해내는것임;)
    if ((remaining - minDist) % 2 !== 0) {
      return;
    }

    for (const [dx, dy, dirName] of dir) {
      const nx = pos[0] + dx;
      const ny = pos[1] + dy;

      if (nx >= 1 && nx <= n && ny >= 1 && ny <= m) {
        dfs([nx, ny], path + dirName);
        if (answer !== "impossible") {
          return;
        }
      }
    }
  }

  dfs(S, "");

  return answer;
}

@github-actions
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
경로 검증 로직 모듈화

최단 거리 계산과 유효성 검사 로직을 별도의 함수로 분리하여 코드의 의도를 명확히 하세요. 이렇게 하면 각 검사의 목적이 더 잘 드러나고 재사용성도
향상됩니다.

Programmers/Level3/150365_미로_탈출_명령어.js [34-46]

-const remaining = k - path.length; // k까지 남은 거리
-const minDist = Math.abs(pos[0] - E[0]) + Math.abs(pos[1] - E[1]);
-if (minDist > remaining) {
-  return;
+function calculateMinDistance(pos) {
+  return Math.abs(pos[0] - E[0]) + Math.abs(pos[1] - E[1]);
 }
-if ((remaining - minDist) % 2 !== 0) {
+
+function isValidRemainingPath(remaining, minDist) {
+  if (minDist > remaining) return false;
+  if ((remaining - minDist) % 2 !== 0) return false;
+  return true;
+}
+
+const remaining = k - path.length;
+const minDist = calculateMinDistance(pos);
+if (!isValidRemainingPath(remaining, minDist)) {
   return;
 }
Suggestion importance[1-10]: 5

__

Why: 최단 거리 계산과 유효성 검사를 분리하는 것은 코드의 의도를 더 명확하게 만들고 각 검증 단계의 목적을 잘 드러내지만, 현재 구조도 이미 주석으로 잘 설명되어 있습니다.

Low
함수 로직 분리로 가독성 개선

dfs 함수의 로직을 더 명확하게 분리하여 가독성을 개선하세요. 도착 지점 체크와 경로 길이 검증을 별도의 헬퍼 함수로 추출하면 코드의 의도가 더
명확해집니다.

Programmers/Level3/150365_미로_탈출_명령어.js [19-31]

+function isDestinationReached(pos) {
+  return pos[0] === E[0] && pos[1] === E[1];
+}
+
 function dfs(pos, path) {
   if (answer !== "impossible") {
     return;
   }
 
-  // 도착한 경우
-  if (pos[0] === E[0] && pos[1] === E[1]) {
-    if (path.length === k) {
-      // 도착하기까지의 경로가 k와 같다면 이것이 답!
-      answer = path;
-      return;
-    }
+  if (isDestinationReached(pos) && path.length === k) {
+    answer = path;
+    return;
   }
Suggestion importance[1-10]: 4

__

Why: 도착 지점 체크를 별도 함수로 분리하는 것은 코드 가독성을 약간 개선하지만, 현재 구조도 충분히 명확하며 함수 분리가 큰 이점을 제공하지 않습니다.

Low
방향 데이터 구조 개선

방향 배열을 객체 형태로 리팩토링하여 각 방향의 의미를 더 명확하게 표현하세요. 이는 코드의 의도를 더 잘 전달하고 유지보수성을 향상시킵니다.

Programmers/Level3/150365_미로_탈출_명령어.js [12-17]

-const dir = [
-  [1, 0, "d"],
-  [0, -1, "l"],
-  [0, 1, "r"],
-  [-1, 0, "u"],
-];
+const DIRECTIONS = {
+  down: { dx: 1, dy: 0, symbol: "d" },
+  left: { dx: 0, dy: -1, symbol: "l" },
+  right: { dx: 0, dy: 1, symbol: "r" },
+  up: { dx: -1, dy: 0, symbol: "u" }
+};
Suggestion importance[1-10]: 3

__

Why: 배열에서 객체로의 변환이 약간의 가독성 향상을 제공하지만, 현재 배열 구조도 충분히 명확하며 DFS 순회에는 배열이 더 적합할 수 있습니다.

Low

@yoouyeon yoouyeon added the ready-to-merge pr을 머지해주세요 label Oct 22, 2025
@uyeon0 uyeon0 merged commit 8d1d032 into main Oct 22, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

programmers Programmers 문제 풀이 ready-to-merge pr을 머지해주세요

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants