https://www.acmicpc.net/problem/1238
1238번: 파티
첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 10,000), X가 공백으로 구분되어 입력된다. 두 번째 줄부터 M+1번째 줄까지 i번째 도로의 시작점, 끝점, 그리고 이 도로를 지나는데 필요한 소요시간 Ti가 들어
www.acmicpc.net
✔ 문제 풀이
조건 분석
- N개의 숫자로 구분된 각각의 마을에 한 명의 학생이 살고 있다.
- 각각의 학생들은 파티 장소 X에 참석하기 위해 걸어가서 다시 그들의 마을로 돌아와야 한다.
- 학생들은 최단 시간에 오고 가기를 원한다.
- 왕복시간이 가장 긴 학생의 소요시간을 구하라.
풀이
학생들은 최단시간에 이동을 원하므로 최단 거리 알고리즘 다익스트라를 생각하였다.
이때, 이동방향에 따라 두 가지로 나누어 볼 수 있다.
1. 집 → 파티 : 각 집에서 파티로 이동하는 최단거리
2. 파티 → 집 : 파티 : 파티에서 각 집으로 이동하는 최단거리
2번의 경우 한번의 다익스트라 수행으로 특정 노드(X)에서 다른 모든 노드들로의 최단거리를 구할 수 있다.
하지만 1번의 경우가 이 문제의 재밌는 지점이다. 단순히 생각한다면 시작위치를 각 노드로 잡고 N-1번의 다익스트라를 수행할 수 있는데 이러한 방법은 누가 봐도 비효율적이다.
* 사실 처음에 이렇게 풀었다가 실행시간이 900ms 가 나왔다..
이제부터가 소개하는 방식으로 실행시간을 200ms로 줄였다.
아이디어는 1번 경우와 2번 경우를 분리시켜 각 경우에서 사용할 간선 정보(list)와 거리 정보(dist)를 따로 생성하는 것이다.
2번 경우(파티→ 집)에는 list, dist.
1번 경우(집→ 파티)에는 reverseList, reverseDist 를 둔다.
간선 정보는 출발점(from), 도착점(to), 가중치(weight) 들로 구성되어 있는데
여기서 출발점과 도착점을 서로 바꾸어 그래프(reverseList)에 등록하는 것이다.
이 reverseList를 기반으로 다익스트라를 수행하여 reverseDist에 값을 채워나가면 한번의 다익스트라 수행으로 각 노드에서 X까지의 거리를 한번에 구할 수 있다.
추가적인 설명은 코드의 주석으로 이어집니다.
✔ Java Code
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static final int INF = 1_000_000_000;
static int N, M, X;
static List<List<Node>> list, reverserList;
static int[] dist, reverseDist; // 파티에서 각 집으로 가는 거리, 각 집에서 파티로 가는 거리
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken()); // 노드 갯수
M = Integer.parseInt(st.nextToken()); // 간선 갯수
X = Integer.parseInt(st.nextToken()); // 파티 장소
list = new ArrayList<>(); // 파티 -> 집
reverserList = new ArrayList<>(); // 집 -> 파티
// 리스트 초기화
for (int i = 0; i <= N; i++) {
list.add(new ArrayList<>());
reverserList.add(new ArrayList<>());
}
// 리스트 정보 등록
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int from = Integer.parseInt(st.nextToken());
int to = Integer.parseInt(st.nextToken());
int weight = Integer.parseInt(st.nextToken());
list.get(from).add(new Node(to, weight));
reverserList.get(to).add(new Node(from, weight)); // 입력값에서의 from과 to를 반전시켜 입력
}
System.out.println(solution());
}
static int solution() {
dist = new int[N+1];
reverseDist = new int[N+1];
// 거리 배열 초기화
Arrays.fill(dist, INF);
Arrays.fill(reverseDist, INF);
// 시작 위치 X 초기화
dist[X] = 0;
reverseDist[X] = 0;
// 최단경로 탐색
dijkstra(list, dist, X);
dijkstra(reverserList, reverseDist, X);
// 집에서 파티로 갔다가 다시 집으로 돌아오는 시간의 최댓값을 탐색
int maxTime = 0;
for (int i = 1; i <= N; i++) {
maxTime = Math.max(maxTime, dist[i] + reverseDist[i]);
}
return maxTime;
}
static void dijkstra(List<List<Node>> graph, int[] dist, int start) {
PriorityQueue<Node> pq = new PriorityQueue<>((x, y) -> x.weight - y.weight);
pq.offer(new Node(start, 0));
while (!pq.isEmpty()) {
Node curNode = pq.poll();
// dist배열에 저장되어 있는 현재 노드까지의 거리보다 현재 노드 객체에 담긴 weight 값이 더 크면 다음 노드 탐색으로 건너뛴다
// 더 진행하여도 dist배열을 갱신할 여지가 없기 때문
if (dist[curNode.to] < curNode.weight) continue;
// 현재 노드의 인접 노드들을 탐색
for (int i = 0; i < graph.get(curNode.to).size(); i++) {
Node adjNode = graph.get(curNode.to).get(i);
// dist 배열의 원소를 더 작은 값으로 갱신할 수 있는 경우
if (dist[adjNode.to] > curNode.weight + adjNode.weight) {
dist[adjNode.to] = curNode.weight + adjNode.weight;
pq.offer(new Node(adjNode.to, dist[adjNode.to]));
}
}
}
}
static class Node {
int to;
int weight; // to 노드까지 가는 데 걸리는 시간
public Node(int to, int weight) {
this.to = to;
this.weight = weight;
}
}
}
'Algorithm > Graph Traversal' 카테고리의 다른 글
[프로그래머스]단어 변환 / 자바 (BFS/DFS 맛꿀마 문제) (1) | 2024.03.23 |
---|---|
[백준] 14503. 로봇 청소기 / Java (0) | 2023.06.08 |
[백준] 11725. 트리의 부모 찾기 / Java (0) | 2023.05.30 |
[백준] 4963. 섬의 개수 / Java (0) | 2023.05.29 |
[백준] 14502 연구소 / java (0) | 2023.05.29 |