알고리즘/이분 탐색

[백준]JAVA - 1939번: 중량제한

K.두부 2023. 5. 4. 18:30
반응형

https://www.acmicpc.net/problem/1939

 

1939번: 중량제한

첫째 줄에 N, M(1 ≤ M ≤ 100,000)이 주어진다. 다음 M개의 줄에는 다리에 대한 정보를 나타내는 세 정수 A, B(1 ≤ A, B ≤ N), C(1 ≤ C ≤ 1,000,000,000)가 주어진다. 이는 A번 섬과 B번 섬 사이에 중량제한이

www.acmicpc.net

🔶 풀이

한 번의 이동에서 옮길 수 있는 물품들의 최대값을 구하시오.

가능한 최대 중량을 찾기 위해서는 bfs로만 풀 수 없고, 이진 탐색을 이용해야한다.

 

1. 입력하는 과정에서 min, max 값을 선언해주고, 다리는 양방향이므로 arrList에 두 번씩 입력해준다.

for (int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine());
int s = Integer.parseInt(st.nextToken());
int e = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
max = Math.max(max, v);
min = Math.min(min, v);
arrList[s].add(new Bridge(e, v));
arrList[e].add(new Bridge(s, v));
}

2. 모든 입력을 완료했으면 min, max 값으로 이진 탐색을 진행한다.

: 중량(mid)를 기준으로 해당 경로를 이용할 수 있는지 여부를 따지면서 진행하면 된다.

 

이동할 수 없음 → 중량이 너무 크다는 뜻이므로 max - 1

이동할 수 있음 → 중량이 너무 작다는 뜻이므로 min + 1, 또한 이동할 수 있는 값이므로 ans 초기화

 

3. 해당 경로를 이용할 수 있는지 여부는 bfs로 진행한다.

: 해당 경로를 안전하게 이용할 수 있다면 true를 반환해주고, 그렇지 않다면 false를 반환해준다.

// 현재 섬에서 갈 수 있는 섬 개수만큼
for (int i=0; i<arrList[now].size(); i++) {
// 다리를 건널 수 있고, 도착 섬에 방문하지 않은 경우
if (arrList[now].get(i).weight >= w && !visited[arrList[now].get(i).endLand]) {
visited[arrList[now].get(i).endLand] = true;
q.add(arrList[now].get(i).endLand);
}
}

재방문 방지를 위해서 visited[] 배열도 선언해주어야함.

 

 

<최종코드>

import java.io.*;
import java.util.*;
public class Main {
static ArrayList<Bridge>[] arrList; // 다리 정보를 담는 배열
static int N, M;
public static class Bridge {
int endLand, weight;
public Bridge(int endland, int weight) {
this.endLand = endland;
this.weight = weight;
}
}
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()); // 다리에 대한 정보
arrList = new ArrayList[N+1];
for (int i=0; i<=N; i++) {
arrList[i] = new ArrayList<>();
}
int max = 0;
int min = Integer.MAX_VALUE;
for (int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine());
int s = Integer.parseInt(st.nextToken());
int e = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
max = Math.max(max, v);
min = Math.min(min, v);
arrList[s].add(new Bridge(e, v));
arrList[e].add(new Bridge(s, v));
}
st = new StringTokenizer(br.readLine());
int sLand = Integer.parseInt(st.nextToken()); // 출발 섬
int eLand = Integer.parseInt(st.nextToken()); // 도착 섬
int ans = 0;
while (max >= min) {
int mid = (max + min) / 2; // 현재 중량
// 현재 중량(mid)로 출발 섬에서 도착 섬으로 갈 수 있는지 여부
if (canGo(sLand, eLand, mid)) {
min = mid + 1;
ans = mid;
} else {
max = mid - 1;
}
}
System.out.println(ans);
}
/**
*
* @param s 출발 섬
* @param e 도착 섬
* @param w 현재 중량
* @return
*/
public static boolean canGo(int s, int e, int w) {
Queue<Integer> q = new LinkedList<>();
boolean[] visited = new boolean[N+1];
q.add(s);
visited[s] = true;
while (!q.isEmpty()) {
int now = q.poll();
// 도착할 경우
if (now == e) {
return true;
}
// 현재 섬에서 갈 수 있는 섬 개수만큼
for (int i=0; i<arrList[now].size(); i++) {
// 다리를 건널 수 있고, 도착 섬에 방문하지 않은 경우
if (arrList[now].get(i).weight >= w && !visited[arrList[now].get(i).endLand]) {
visited[arrList[now].get(i).endLand] = true;
q.add(arrList[now].get(i).endLand);
}
}
}
return false;
}
}

 

 

반응형