오늘 풀어볼 문제는 프로그래머스의 GPS 입니다.
⭐문제링크
- 택시는 멈출 수 있다.
- 왔던 길을 되돌아 갈 수도 있다.
- 한 거점에 머무를 수 있다.
해당 문제를 보고 처음엔 이런 식으로 로직을 짰었음
[ 로직 생각 ]
1. 택시의 이동 경로를 따라 앞과 뒤에서 투 포인터로 갈 수 없는 경로 파악
1.1 갈 수 없는 경로? 현재 거점이 이전 거점으로부터 움직임의 횟수가 2 이상인 것
- 투 포인터의 두 거점의 거리차를 계산
2.1 움직임의 횟수가 2번이라면, 중간 지점 1개만 넣어주면 된단 의미
- 시작점에서 시작한 첫 번째 포인터는 본인 포함 후 인덱스 +로 두 지점의 거리차 인덱스까지 조사해서 경우의 수 구하기
- 끝에서 시작한 첫 번째 포인터는 본인 포함 후 인덱스 -로 두 지점의 거리차 인덱스까지 조사해서 경우의 수 구하기
- 3과 4에서 구한 경우의 수 중 가장 최소의 수가 정답.
[ 함수 ]
3과 4구현 위해 다익스트라가 생각남.
그러나 내 로직이 틀린 점은 다음과 같음
❌투 포인터를 사용하는 것은 옳지 않음
❌거리 기반 수정 방식은 문제의 본질이 아님.
❌다익스트라를 사용하는 것은 효율적이지 않음
해당 문제는 DP임!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[로직 설계]
1. 이중 for문을 돎
- 첫 번째 for 문: 시간의 흐름
- 두 번째 for 문: 모든 노드 접근 후 로직 수행
- 각 시간대(i)마다 가능한 모든 이동을 고려.
2.1 제자리에 머무르는 경우
2.2 인접 노드로 이동할 경우
이동을 고려하는 로직은 다음과 같음
(참고로 시작점을 제외한 모든 dp 배열의 값은 Integer.MAX_VALUE임)
(참고로2 해당 코드는 택시가 머무를 경우에 대한 로직임)
dp[time][cur] = Math.min(dp[time][cur],
dp[i - 1][cur] + (gps_log[time] == cur ? 0 : 1));
현재 시각의 접근한 노드의 DP 배열은 다음 두 값중 작은 값을 넣어주면서 갱신되어감
- 현재 시각의 접근한 노드의 원래 DP배열 값
- 이전 시간에서 접근한 해당 노드의 값 + 현재 접근한 노드가 GPS 상에서 올바르게 찍혀있었는지(맞으면 0, 아니면 1)
위 코드는 택시가 머무를 경우의 로직이었지만, 택시가 인접 노드로 움직일 경우 위와 동일한 코드로 인접한 모든 노드의 DP값을 갱신해주면서 가면 최종적으로는 모든 DP배열이 특정 시간까지 쌓인 최소 수정 횟수를 알 수 있음
import java.util.*;
class Solution {
static List<Integer>[] graph;
public int solution(int n, int m, int[][] edge_list, int k, int[] gps_log) {
graph = new ArrayList[n + 1];
for (int i = 0; i <= n; i++) {
graph[i] = new ArrayList<>();
}
for (int i = 0; i < m; i++) {
int from = edge_list[i][0];
int to = edge_list[i][1];
graph[from].add(to);
graph[to].add(from);
}
return findRetouch(n, k, gps_log);
}
static int findRetouch(int n, int k, int[] gps_log) {
int[][] dp = new int[k][n + 1]; //i번째 시간대에 j번 거점에 도착했을 당시 수정된 횟수
for (int[] row : dp) Arrays.fill(row, Integer.MAX_VALUE);
dp[0][gps_log[0]] = 0; // 시작 지점 설정
for (int time = 1; time < k; time++) {
for (int cur = 1; cur <= n; cur++) {
if (dp[time - 1][cur] == Integer.MAX_VALUE) continue; // 이전에 도달할 수 없던 경우 패스
// 1️⃣ 제자리에 머무르는 경우
dp[time][cur] = Math.min(dp[time][cur], dp[i - 1][cur] + (gps_log[time] == cur ? 0 : 1));
// 2️⃣ 인접한 노드로 이동하는 경우
for (int next : graph[cur]) {
dp[time][next] = Math.min(dp[time][next], dp[i - 1][cur] + (gps_log[time] == next ? 0 : 1));
}
}
}
int answer = dp[k - 1][gps_log[k - 1]];
return answer == Integer.MAX_VALUE ? -1 : answer;
}
}