문제 설명
정수 배열 arr가 주어졌을 때, 우리는 처음에 인덱스 0에 위치해 있습니다. 한 번의 점프로 다음 세 가지 이동이 가능합니다.
i + x: 단,i + x < n(오른쪽으로 이동)i - x: 단,i - x >= 0(왼쪽으로 이동)j:arr[i]와arr[j]의 값이 같고i != j인 임의의 인덱스로 이동
여기서 n은 배열의 크기입니다. 우리가 구해야 하는 것은 배열의 마지막 인덱스(n-1)에 도달하기 위한 최소 점프 횟수입니다.
입력 예시
입력이 다음과 같다고 가정해 보겠습니다.
{20, -5, -5, 25, 20, 5, 5, 5, 1, 25}이때 출력은 3입니다. 인덱스 0 → 4 → 3 → 9 순서로 딱 세 번의 점프만 하면 마지막 인덱스에 도달할 수 있기 때문입니다.
- 인덱스 0(값 20) → 인덱스 4(값 20) : 같은 값을 가진 인덱스로 점프
- 인덱스 4 → 인덱스 3 : 한 칸 왼쪽으로 이동
- 인덱스 3(값 25) → 인덱스 9(값 25) : 같은 값을 가진 인덱스로 점프하여 도착
풀이 접근: 너비 우선 탐색(BFS)
이 문제는 그래프 탐색 관점에서 바라보면 명확해집니다. 각 인덱스를 노드로, 이동 가능한 경로를 간선으로 생각하면 '인덱스 0에서 인덱스 n-1까지의 최단 거리'를 구하는 문제와 동일합니다. 따라서 BFS(너비 우선 탐색)를 사용하면 레벨 단위로 탐색하면서 최소 점프 횟수를 보장받을 수 있습니다.
알고리즘 단계
- 값을 키(key)로, 해당 값이 등장하는 인덱스들의 목록을 값(value)으로 갖는 맵
m을 정의합니다. n := arr의 크기i를 0부터n-1까지 순회하며m[arr[i]]의 끝에i를 삽입합니다.- 방문 집합
visited에 0을 삽입하고, 큐q에도 0을 넣습니다. lvl := 0부터 시작하여 큐가 빌 때까지 레벨을 1씩 증가시키며 반복합니다.sz := 큐의 현재 크기sz가 0이 될 때까지 다음을 반복합니다.curr := 큐의 맨 앞 요소를 꺼냅니다.curr == n - 1이라면lvl을 반환합니다.i := curri - 1 >= 0이고 아직 방문하지 않았다면i - 1을 큐에 넣고 방문 처리합니다.i + 1 < n이고 아직 방문하지 않았다면i + 1을 큐에 넣고 방문 처리합니다.m[arr[curr]]에 담긴 모든 인덱스j에 대해, 아직 방문하지 않았다면 큐에 넣고 방문 처리합니다.- 처리가 끝난 후
m에arr[curr]가 존재하면 해당 키를 삭제합니다.
- 큐가 비었는데도 도달하지 못했다면
-1을 반환합니다.
핵심 최적화 포인트
주목할 부분은 특정 값에 속한 모든 인덱스를 큐에 넣은 직후 맵에서 해당 키를 삭제하는 것입니다. 같은 값이 수천 번 등장하는 배열에서는 매번 동일한 인덱스 목록을 다시 순회하게 되어 최악의 경우 O(n²) 시간이 걸릴 수 있습니다. 이미 처리한 값의 목록을 제거하면 불필요한 반복을 없애고 전체 시간 복잡도를 O(n) 수준으로 유지할 수 있습니다.
C++ 구현 예제
아래 코드를 통해 더 잘 이해해 보겠습니다.
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int minJumps(vector<int>& arr) {
map<int, vector<int> > m;
int n = arr.size();
for (int i = 0; i < n; i++) {
m[arr[i]].push_back(i);
}
set<int> visited;
visited.insert(0);
queue<int> q;
q.push(0);
for (int lvl = 0; !q.empty(); lvl++) {
int sz = q.size();
while (sz--) {
int curr = q.front();
q.pop();
if (curr == n - 1)
return lvl;
int i = curr;
if (i - 1 >= 0 && !visited.count(i - 1)) {
q.push(i - 1);
visited.insert(i - 1);
}
if (i + 1 < n && !visited.count(i + 1)) {
q.push(i + 1);
visited.insert(i + 1);
}
for (int j = 0; j < m[arr[curr]].size(); j++) {
if (!visited.count(m[arr[curr]][j])) {
q.push(m[arr[curr]][j]);
visited.insert(m[arr[curr]][j]);
}
}
if (m.count(arr[curr])) {
m.erase(arr[curr]);
}
}
}
return -1;
}
};
main(){
Solution ob;
vector<int> v = {20,-5,-5,25,20,5,5,5,1,25};
cout << (ob.minJumps(v));
}실행 결과
입력
{20,-5,-5,25,20,5,5,5,1,25}출력
3
마무리
점프 게임 IV는 BFS의 레벨 단위 탐색과 맵 자료구조를 결합해 풀어야 하는 대표적인 그래프 최단 경로 문제입니다. 인접 이동(i ± 1)과 동일 값 점프(arr[j] == arr[i])라는 두 종류의 간선을 모두 고려하고, 방문 처리와 맵 삭제 최적화를 함께 적용하면 어떤 입력에 대해서도 효율적으로 최소 점프 횟수를 구할 수 있습니다.