🧫 문제 분석
✔️ 출처
📖 문제

최단 경로 + 최대신장(최소 신장의 반대라서 이렇게 붙였다) + 이분탐색 문제다.
근데 본인은 최대 신장 트리로 안풀고, 현재 이분탐색 문제를 공부중이므로 이분탐색 + BFS 최단경로로 풀었다.
이분탐색으로 찾고자 하는 것은 두 섬 사이의 경로 이동 비용 최댓값이다.
가장 비용이 큰 값을 max로 잡고
1 ~ max 사이를 이분탐색하면서 mid값으로 BFS 탐색을 한다.
BFS 탐색에서는 공장이 있는 섬A에서부터 탐색을 하되 mid 값 (중량)을 다리의 중량제한이 버틸 수 있다면 다음 다리로 탐색한다.
공장이 있는 섬 B에 도착하면 현재 mid 값의 중량은 버틴다는 의미이므로 true를 반환해 알린다.
이분탐색에서는 BFS 결과가 true이면 mid 값을 더 키워서 최대 중량값을 찾는다.
최대신장트리로 푸는 방법
크루스칼 알고리즘을 이용한다.
공장이 있는 각 섬의 부모가 같다면 그때 연결한 섬의 비용이 최대 비용이라고 볼 수 있다.
- 비용 내림차순 정렬을 했고, 큰 비용부터 하나씩 최대 비용 간선을 연결한다.
- 연결하면서 공장이 있는 각 섬의 부모를 체크해서 두 섬의 부모가 같다면 현재 연결한 간선의 비용이 무게 최댓값인데
- 이이상 더 큰 값이 최댓값일 수가 없다. 그 뒤에 연결하는 간선들의 비용은 내림차순이기에 점점 줄어들기 떄문이다.
🔅 문제 풀이
import java.io.*;
import java.util.*;
public class Main {
static class Island {
int next;
int cost;
public Island (int next, int cost) {
this.next = next;
this.cost = cost;
}
}
static ArrayList<Island>[] graph;
static boolean[] visited;
static int max = 0;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken()); // 섬 개수
int m = Integer.parseInt(st.nextToken()); // 다리 개수
graph = new ArrayList[n + 1];
visited = new boolean[n + 1];
// 섬 그래프로 초기화
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int A = Integer.parseInt(st.nextToken()); // 섬1
int B = Integer.parseInt(st.nextToken()); // 섬2
int C = Integer.parseInt(st.nextToken()); // 비용
if (graph[A] == null) graph[A] = new ArrayList<>();
if (graph[B] == null) graph[B] = new ArrayList<>();
graph[A].add(new Island(B, C));
graph[B].add(new Island(A, C));
max = Math.max(max, C); // 탐색 범위 max 값
}
// 비용 내림차순 정렬
for (ArrayList<Island> list : graph) {
if (list != null) {
Collections.sort(list, (o1, o2) -> o2.cost - o1.cost);
}
}
// 공장이 있는 두 섬
st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
// 비용 이분탐색
int front = 1, back = max;
int max = 0;
while (front <= back) {
int mid = (front + back) >>> 1;
if (bfs(start, end, mid)) {
front = mid + 1;
max = mid;
} else {
back = mid - 1;
}
visited = new boolean[n + 1];
}
System.out.println(max);
}
// bfs로 공장이 있는 A -> B로 가는 길에 mid값으로 갈 수 있는 지 확인
private static boolean bfs(int start, int end, int cost) {
Queue<Island> q = new LinkedList<>();
q.add(new Island(start, 0));
visited[start] = true;
while (!q.isEmpty()) {
Island curIsland = q.poll();
// 반대편 공장이 있는 섬에 도착했다면 true 반환
if (curIsland.next == end) {
return true;
}
ArrayList<Island> list = graph[curIsland.next];
for (int i = 0; i < list.size(); i++) {
Island nextIsland = list.get(i);
// 방문하지 않았으면서, cost로 이동 가능하면 큐에 추가
if (!visited[nextIsland.next] && nextIsland.cost >= cost) {
q.add(nextIsland);
visited[nextIsland.next] = true;
}
}
}
return false;
}
}
🔅 크루스칼 알고리즘 풀이
import java.io.*;
import java.util.*;
import java.util.stream.IntStream;
public class Main {
static int[][] graph;
static int[] parent;
static int min = Integer.MAX_VALUE;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken()); // 섬 개수
int m = Integer.parseInt(st.nextToken()); // 다리 개수
graph = new int [m][3];
parent = IntStream.range(0, n + 1).toArray(); // 부모
// 섬 그래프로 초기화
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
graph[i][0] = Integer.parseInt(st.nextToken()); // 섬1
graph[i][1] = Integer.parseInt(st.nextToken()); // 섬2
graph[i][2] = Integer.parseInt(st.nextToken()); // 비용
}
st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
// 비용 내림차순 정렬
Arrays.sort(graph, (o1, o2) -> o2[2] - o1[2]);
for (int i = 0; i < m; i++) {
if (find(graph[i][0]) != find(graph[i][1])) { // 서로 다른 부모 즉, 사이클이 생기지 않는다면
union(graph[i][0], graph[i][1]);
if (find(parent[start]) == find(parent[end])) { // 시작과 끝점의 부모가 같다면 현재 비용이 이동가능한 무게 최댓값
min = graph[i][2];
break;
}
}
}
System.out.println(min);
}
private static void union(int a, int b) {
a = find(a);
b = find(b);
if (a > b) {
parent[a] = b;
} else {
parent[b] = a;
}
}
private static int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]); // 경로압축
}
}
❗ 오답노트 / 필요한 지식
'baekjoon > BinarySearch' 카테고리의 다른 글
백준 16401번 : 과자 나눠주기 [자바] (0) | 2025.03.03 |
---|---|
백준 12015번 : 가장 긴 증가하는 부분 수열 2 [자바] (0) | 2025.02.28 |
백준 2512번 : 예산 [자바] (0) | 2025.02.28 |
백준 14502번 : 연구소 자바 (3) | 2024.09.02 |
백준 2805번 : 나무 자르기 자바 (0) | 2024.08.30 |