https://www.acmicpc.net/problem/1939
골드3
구현 방법
1. 문제 조건
- N: 섬의 수 (2 ≤ N ≤ 10,000)
- M: 다리의 수 (1 ≤ M ≤ 100,000)
- C: 다리의 최대 중량 (1 ≤ C ≤ 1,000,000,000)
- 두 섬 사이의 여러 경로 중, 한 번의 이동에서 옮길 수 있는 물품들의 최대 중량을 구해야 한다.
- 다리마다 중량 제한이 존재하며, 이를 초과하면 경로로 사용할 수 없다.
2. 완전탐색의 비효율성
- 만약 BFS를 통해 모든 가능한 경로를 탐색하는 방식으로 접근하면:
- 시간 복잡도는 최악의 경우 O(2^M)에 도달할 수 있다.
- 이유: 각 다리를 선택하거나 선택하지 않는 경우의 수를 모두 고려해야 하기 때문
- 이는 M=100,000일 경우 비현실적으로 많은 계산을 요구하기 때문에 적합하지 않다.
3. 매개변수 탐색을 통한 효율적 접근
- 이 문제는 완전탐색 대신 **매개변수 탐색(이진 탐색)**을 통해 해결할 수 있다.
- 핵심 아이디어:
- 옮길 수 있는 물품의 중량(최대 중량 제한)을 1∼C 범위로 설정하고, 이 값을 이진 탐색으로 줄여나간다.
- 특정 중량 제한에서 BFS 또는 DFS를 사용하여 두 섬을 연결하는 경로가 존재하는지 확인한다.
- 중량 제한을 만족하는 경로가 있다면, 더 큰 중량으로 범위를 조정하여 최대값을 탐색한다.
- 중량 제한을 만족하지 못한다면, 범위를 줄여가며 탐색을 반복한다.
4. 시간 복잡도 분석
- 이진 탐색: 중량 제한의 범위를 1∼C로 설정하므로 O(logC)의 시간 복잡도를 가짐.
- BFS/DFS 탐색: 한 번의 탐색에서 노드와 간선을 모두 방문하므로 O(N+M)의 시간 복잡도를 가짐.
- 따라서, 전체 시간 복잡도
- O(logC⋅(N+M))
5. 매개변수 탐색이 적합한 이유
- 매개변수 탐색은 "안 되는 기준"과 "되는 기준"을 명확히 구분할 수 있는 문제에서 효과적으로 사용할 수 있다.
- 이 문제에서는:
- 특정 중량 제한을 기준으로 BFS/DFS를 통해 경로가 존재하는지 여부를 확인할 수 있다.
- 경로가 존재하지 않으면 "안 되는 기준", 존재하면 "되는 기준"으로 판단하여 탐색 범위를 좁힐 수 있다.
- 이를 통해 모든 경로를 완전탐색하지 않고도 정답에 도달할 수 있다.
코드
package boj;
import java.io.*;
import java.util.*;
/**
* 백준 1939
* 중량제한
* 골드3
* https://www.acmicpc.net/problem/1939
*/
public class Boj1939 {
private static class Bridge {
int d;
int w;
Bridge(int d, int w) {
this.d = d;
this.w = w;
}
}
private static int N; // 섬 개수
private static int M; // 다리 개수
private static List<Bridge>[] bridges; // 다리의 최대 중량
private static int f1; // 공장1
private static int f2; // 공장2
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());
bridges = new List[N + 1];
for(int i = 1; i <= N; i++)
bridges[i] = new ArrayList<>();
int max = 1;
for(int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int A = Integer.parseInt(st.nextToken());
int B = Integer.parseInt(st.nextToken());
int C = Integer.parseInt(st.nextToken());
bridges[A].add(new Bridge(B, C));
bridges[B].add(new Bridge(A, C));
max = Math.max(max, C);
}
st = new StringTokenizer(br.readLine());
f1 = Integer.parseInt(st.nextToken());
f2 = Integer.parseInt(st.nextToken());
int start = 1;
int end = max;
int result = 1;
while(start <= end) {
int mid = (start + end) >>> 1;
if(bfs(mid)) {
result = mid;
start = mid + 1;
} else {
end = mid - 1;
}
}
System.out.println(result);
}
public static boolean bfs(int target) {
Queue<Integer> queue = new LinkedList<>();
boolean[] visited = new boolean[N + 1];
queue.add(f1);
visited[f1] = true;
while (!queue.isEmpty()) {
int current = queue.poll();
if (current == f2) {
return true; // f1에서 f2로 이동 가능
}
for (Bridge bridge : bridges[current]) {
if (!visited[bridge.d] && bridge.w >= target) {
visited[bridge.d] = true;
queue.add(bridge.d);
}
}
}
return false; // target로는 이동 불가
}
}
'알고리즘' 카테고리의 다른 글
자바 - 백준 1520 / 내리막 길 (1) | 2025.02.21 |
---|---|
자바 - 백준 1027 / 고층 건물 (0) | 2025.02.07 |
자바 - 백준 11054 / 가장 긴 바이토닉 부분 수열 (3) | 2024.12.26 |
자바 - 백준 15663 / N과 M (9) (0) | 2024.12.26 |
자바 - 백준 9252 / LCS 2 (2) | 2024.12.21 |