https://www.acmicpc.net/problem/2307
다익스트라 응용
문제의 추가설명 4번을 유심히 보자
용의자는 검문을 피해서 가장 빨리 도시를 빠져나가고자 하고, 경찰은 적절한 도로를 선택하여 이 용의자들의 탈출시간을 최대한 지연시키고자 한다.
이 문구를 통해서 용의자는 경찰이 배치되지 않은 경로로 항상 최단거리로 이동한다. 라는 것을 알 수 있다.
경찰은 적절한 도로를 선택하여 이 용의자들의 탈출시간을 최대한 지연시키고자 한다.
라는 말은 조금 생각해보면, 적절한 도로를 선택하여 탈출 시간을 지연시키기 위해서는
당연히 용의자가 최단경로로 이동했던 길 중 하나를 선택하여 막아야 지연이 발생한다는 것을 알 수 있다.
따라서 기본 아이디어를 정리하자면 아래와 같다.
1. 다익스트라를 통해 1->N 까지 최단거리 탐색
a. 최단거리 탐색과정에서의 경로 기록 필요
2. 최단 거리의 이동 경로를 바탕으로 경찰이 막을 도로 선정
3. 다익스트라를 통해 1->N 까지 최단거리 탐색
4. 지연된 시간 비교
이 문제의 핵심은
당신은 다익스트라를 구현할 수 있습니까? 가 아니라
당신은 다익스트라의 경로를 추출할 수 있습니까? 를 묻는 것이다.
다익스트라의 경로를 찾으려면 어떻게 할 수 있을지 생각해보자.
private static void dijkstra(int start, int end) {
PriorityQueue<Node> pq = new PriorityQueue<>();
Arrays.fill(dist, INF);
dist[start] = 0;
pq.add(new Node(1, 0));
while (!pq.isEmpty()) {
Node cur = pq.poll();
if (cur.to == end) // 목적지 도달
break;
for (Node next : graph[cur.to]) {
if (dist[next.to] > dist[cur.to] + next.weight) {
dist[next.to] = dist[cur.to] + next.weight; // 비용 갱신
pq.add(new Node(next.to, dist[next.to]));
}
}
}
}
일반적으로 다익스크라 구현을 했다면 위와 같은식으로 구현이 된다.
여기서 아이디어를 생각해보자면,
다음 목적지로의 비용을 갱신하는 과정에서 이전에 어디서 왔는지를 기록하면 된다.
private static void dijkstra(int start, int end, int[] disable) {
PriorityQueue<Node> pq = new PriorityQueue<>();
Arrays.fill(dist, INF);
dist[start] = 0;
pq.add(new Node(1, 0));
while (!pq.isEmpty()) {
Node cur = pq.poll();
if (cur.to == end) // 목적지 도달
break;
for (Node next : graph[cur.to]) {
// 현재 위치에서 다음 위치로 가는길에 검문이 있다. -> 지나갈 수 없음
if (cur.to == disable[0] && next.to == disable[1])
continue;
if (dist[next.to] > dist[cur.to] + next.weight) {
dist[next.to] = dist[cur.to] + next.weight; // 비용 갱신
prev[next.to] = cur.to; // 경로 기록
pq.add(new Node(next.to, dist[next.to]));
}
}
}
}
위와 같은 식으로 기록을 했다면, N번 노드로 가기위해 바로 직전 방문했던 노드가 prev[N]번 노드임을 알 수 있다.
이제 어떤 경로로 이동했는지 파악이 가능하므로, 각 경로를 하나씩 막으면서 다시 다익스트라 알고리즘을 수행하며 거리를 파악하자.
새롭게 산출된 값과 기존의 값을 비교하며 얼마나 지연시킬 수 있는지 확인한다면 끝이다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;
public class Main {
static class Node implements Comparable<Node> {
int to;
int weight;
Node(int to, int weight) {
this.to = to;
this.weight = weight;
}
@Override
public int compareTo(Node o) {
return this.weight - o.weight;
}
}
static List<Node>[] graph;
static int N, M;
static int[] dist, prev;
static int INF = 987654321;
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader((System.in)));
String[] inputs = in.readLine().split(" ");
N = stoi(inputs[0]);
M = stoi(inputs[1]);
graph = new List[N + 1];
for (int i = 0; i <= N; ++i)
graph[i] = new ArrayList<>();
for (int i = 0; i < M; ++i) {
inputs = in.readLine().split(" ");
int p1 = stoi(inputs[0]);
int p2 = stoi(inputs[1]);
int cost = stoi(inputs[2]);
graph[p1].add(new Node(p2, cost));
graph[p2].add(new Node(p1, cost));
}
dist = new int[N + 1];
prev = new int[N + 1];
dijkstra(1, N, new int[] {0, 0});
// 지나왔던 경로를 바탕으로, 이동할 수 없는 도로를 생성.
List<int[]> pathList = new ArrayList<>();
for (int i = N; prev[i] != 0; i = prev[i])
pathList.add(new int[] {prev[i], i});
int max = 0;
int orgTime = dist[N];
for (int[] path : pathList) {
dijkstra(1, N, path);
int fixTime = dist[N];
// 도시를 빠져나가지 못하게 만들 수 있는 경우
if (fixTime == INF) {
max = -1;
break;
}
max = Math.max(max, fixTime - orgTime);
}
System.out.println(max);
}
private static void dijkstra(int start, int end, int[] disable) {
PriorityQueue<Node> pq = new PriorityQueue<>();
Arrays.fill(dist, INF);
dist[start] = 0;
pq.add(new Node(1, 0));
while (!pq.isEmpty()) {
Node cur = pq.poll();
if (cur.to == end) // 목적지 도달
break;
for (Node next : graph[cur.to]) {
// 현재 위치에서 다음 위치로 가는길에 검문이 있다. -> 지나갈 수 없음
if (cur.to == disable[0] && next.to == disable[1])
continue;
if (dist[next.to] > dist[cur.to] + next.weight) {
dist[next.to] = dist[cur.to] + next.weight; // 비용 갱신
prev[next.to] = cur.to; // 경로 기록
pq.add(new Node(next.to, dist[next.to]));
}
}
}
}
private static int stoi(String s) {
return Integer.parseInt(s);
}
}