공유기 설치
문제
도현이의 집 N개가 수직선 위에 있다. 각각의 집의 좌표는 x1, ..., xN이고, 집 여러개가 같은 좌표를 가지는 일은 없다.
도현이는 언제 어디서나 와이파이를 즐기기 위해서 집에 공유기 C개를 설치하려고 한다. 최대한 많은 곳에서 와이파이를 사용하려고 하기 때문에, 한 집에는 공유기를 하나만 설치할 수 있고, 가장 인접한 두 공유기 사이의 거리를 가능한 크게 하여 설치하려고 한다.
C개의 공유기를 N개의 집에 적당히 설치해서, 가장 인접한 두 공유기 사이의 거리를 최대로 하는 프로그램을 작성하시오.
입력
첫째 줄에 집의 개수 N (2 ≤ N ≤ 200,000)과 공유기의 개수 C (2 ≤ C ≤ N)이 하나 이상의 빈 칸을 사이에 두고 주어진다. 둘째 줄부터 N개의 줄에는 집의 좌표를 나타내는 xi (0 ≤ xi ≤ 1,000,000,000)가 한 줄에 하나씩 주어진다.
출력
첫째 줄에 가장 인접한 두 공유기 사이의 최대 거리를 출력한다.
풀이
이 문제는 적절한 집에 c개의 와이파이를 설치하였을 때 와이파이 사이의 가장 인접한 와이파이 사의 거리가 최대인 값을 찾는 문제이다.
즉 집들 중에서 특정 c개의 집을 뽑았을 때 그 사이의 거리가 그 집들중 가장 인접한 집들 사이의 거리가 최대인 경우를 찾는 것이다.
이렇게 설명하면 조금 어렵게 느껴질 수 있는데 알고리즘 문제에서는 입력값에 힌트가 많이 있다.
집의 개수 N과 와이파이의 갯수 c 는 2<= c <= n <= 200,000이다. 이기 때문에 여러 알고리즘을 생각할 수 있다.
그러나 집의 좌표를 나타내는 xi는 범위가 1,000,000,000으로 상당히 크기때문에 log n의 시간복잡도를 가지는 이분탐색을 생각해야 한다.
어차피 정답은 와이파이 사이의 거리이다.
그럼 이 문제에서 정답의 범위는 xi이다.
정답의 범위는 0<= x <= 1,000,000,000이므로 여기서 힌트를 얻고 잘 생각해보자.
이분탐색으로 풀어야 한다는 힌트를 얻고, 입력이 주어졌을 때 정답의 범위를 알기 때문에 이분탐색의 범위는
시작값(start)은 입력들 중 최솟값,
마지막값(end)는 입력들 중 최댓값-최솟값으로 구할 수 있다.
이분탐색의 기본 구조를 따라가며 mid = (start + end) / 2를 구한다.
이 때 mid는 공유기 사이의 거리를 의미한다.
집 들 사이의 거리가 mid이상일 때 공유기를 설치한다고 가정했을 때 설치 되는 공유기의 수를 센다.(cnt)
이 때 cnt가 입력값 c보다 작다면 공유기 사이의 거리(mid)가 큰 것이므로 범위를 줄여 재 탐색한다.
혹은 cnt가 입력값 c보다 크거나 같다면 공유기 사이의 거리(mid)가 작은 것이므로 범위를 늘여 재 탐색한다. (최댓값을 찾아야 하기 때문)
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main{
public static void main(String\[\] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());
int\[\] arr = new int\[n\];
for (int i = 0; i < n; i++) {
arr\[i\] = Integer.parseInt(br.readLine());
}
Arrays.sort(arr);
int start = 1;
int end = arr\[n - 1\] - arr\[0\];
int answer = 0;
// 이분탐색
while(start <= end){
int mid = (start + end) / 2;
int cnt = 1;
int lastWifi = arr\[0\];
for (int i = 1; i < n; i++) {
if (arr\[i\] >= lastWifi + mid) {
cnt++;
lastWifi = arr\[i\];
}
}
if(cnt >= c){
start = mid + 1;
// wifi 갯수가 모자란데 mid값이 답이 되면 안되므로 크거나 같을때만 저장
answer = mid;
}else{
end = mid - 1;
}
}
System.out.println(answer);
}
}
'알고리즘' 카테고리의 다른 글
1715 카드 정렬하기 (0) | 2022.03.25 |
---|---|
10825 국영수 (0) | 2022.03.25 |
1074 Z (0) | 2022.03.25 |
2293 동전 1 (0) | 2022.03.25 |
2155 포도주 시식 (0) | 2022.03.25 |