🔗 2110번 : 공유기 설치
● Java 풀이
● 시간 제한 2초
● 메모리 제한 128 MB
문제
도현이의 집 N개가 수직선 위에 있다. 각각의 집의 좌표는 x1, ..., xN이고, 집 여러개가 같은 좌표를 가지는 일은 없다.
도현이는 언제 어디서나 와이파이를 즐기기 위해서 집에 공유기 C개를 설치하려고 한다. 최대한 많은 곳에서 와이파이를 사용하려고 하기 때문에, 한 집에는 공유기를 하나만 설치할 수 있고, 가장 인접한 두 공유기 사이의 거리를 가능한 크게 하여 설치하려고 한다.
C개의 공유기를 N개의 집에 적당히 설치해서, 가장 인접한 두 공유기 사이의 거리를 최대로 하는 프로그램을 작성하시오.
테스트 케이스
//input
5 3
11
12
18
14
19
//answer
3
// input
2 2
0
1
// answer
1
풀이
import java.io.*;
import java.util.*;
public class Main {
private static int N, C;
private static int[] houses;
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());
C = Integer.parseInt(st.nextToken());
houses = new int[N];
for (int i = 0; i < N; i++) {
houses[i] = Integer.parseInt(br.readLine());
}
Arrays.sort(houses);
int start = houses[0], end = houses[N - 1] + 1, mid;
while(start < end) {
mid = (start + end) / 2; // 최소 거리 이분 탐색
if(findMinDist(mid - houses[0]) < C) { // 공유기 개수보다 작게 설치 되었을 때
//System.out.printf("최소 거리가 %2d일 때, 공유기 설치 불가능...\n", mid - houses[0]);
end = mid;
} else { // 같거나 많을 때
//System.out.printf("최소 거리가 %2d일 때, 공유기 설치 가능!\n", mid - houses[0]);
start = mid + 1; // 거리 조금씩 넓혀가보기 (start = 마지막 공유기 설치 가능 거리)
}
}
System.out.println(start - 1 - houses[0]);
}
private static long findMinDist(int dist) { // 최소 거리 설정
int count = 1; // 첫 집 + 1
int lastRout = houses[0]; // 첫집 선택
for (int i = 1; i < N; i++) {
if(dist <= houses[i] - lastRout) { // 최소거리 이상일 때
count++;
lastRout = houses[i];
}
}
//System.out.printf("최소 거리가 %2d일 때, 공유기 설치 가능 개수는 %2d개\n", dist, count);
return count;
}
}
Upper Bound를 알아야 풀 수 있는 문제였다.
Upper Bound란 집합 내 특정 조건을 만족하는 요소의 최대값을 찾는 알고리즘으로,
이분 탐색을 기반으로 작동한다.
범위를 반으로 줄여나가며, 결과적으로 원하는 값의 1 인덱스 초과한 값을 찾아낸다. (left = mid + 1)
public int upperBound(int[] arr, int target) {
int left = 0;
int right = arr.length; // right는 length - 1이 아닌 length로 설정된다
int mid;
while(left < right) {
mid = (left + right) / 2; // 중간 값
if(arr[mid] <= target) { // 중간 값이 목표보다 작거나 같을 시 (목표 충족 시)
left = mid + 1; // 왼쪽 범위 늘리기
} else { // 중간 값이 목표보다 클 시 (목표 미충족 시)
right = mid; // 오른쪽 범위 줄이기
}
}
return left - 1; // (결과 값의 1 인덱스 초과한 값) - 1
}
여기서 응용해야 할 것은, 그리고 사람들이 많이 틀리는 부분은
- right 값의 잘못된 초기화. right는 끝 인덱스가 아닌 것을 명심해야 한다.
- 문제의 응용. 문제는 인덱스로 판별하는 것이 아니라, 집의 거리로 판단하는 것.
위 테스트 케이스가 대표적인 반례로 꼽힐 수 있을 것 같다.
단순히 Upper Bound 알고리즘을 외워 적용하는 것이 아닌, 응용하는 것이 이 문제의 핵심이다.
공유기를 서로 멀리 떨어뜨리고, 떨어뜨린 공유기 중 가장 인접한 거리를 구하는 문제이다.
그러므로 문제의 이해 방식은,
가장 인접한 거리를 이분 탐색으로 찾아보고 그만큼, 혹은 그보다 더 떨어진 거리에 공유기를 설치 해본 후 (findMinDist)
공유기 개수가 설치 가능 개수를 넘었을 경우 해당 최소 거리에 공유기를 설치할 수 있다고 보는 것이다. (Upper Bound)
나 같은 경우엔 Upper Bound 알고리즘 자체를 처음 접해서,
분하지만... Upper Bound에 대한 이해를 마치고 이를 적용하여 문제를 풀었다.
앞으로 정렬 탐색에서 특정 조건에 최댓값 최솟값을 찾을 때 Upper Bound와 Lower Bound를 떠올릴 수 있는 연습이 필요할 것 같다.
'알고리즘 및 데이터 구조 > 백준' 카테고리의 다른 글
[백준] 1516번 : 게임 개발 (Java) (0) | 2024.03.16 |
---|---|
[백준] 16235번 : 나무 재테크 (Java) (0) | 2024.03.14 |
[백준] 1600번 : 말이 되고픈 원숭이 (Java) (0) | 2024.03.10 |
[백준] 15685번 : 드래곤 커브 (Java) (0) | 2024.03.03 |
[백준] 15684번 : 사다리 조작 (Java) (2) | 2024.03.01 |