이번 문제는 신기하게도 풀이가 두 가지가 있어서 둘 다 작성해보았다.
1) 우선순위 큐를 사용하는 방법
2) 이분탐색을 이용한 방법
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int N, M, L;
static PriorityQueue<Path> pq = new PriorityQueue<>();
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());
L = Integer.parseInt(st.nextToken());
if (N != 0) {
st = new StringTokenizer(br.readLine());
int[] rest = new int[N];
for (int i = 0; i < N; i++) {
rest[i] = Integer.parseInt(st.nextToken());
}
Arrays.sort(rest);
// 길이와 나눈횟수(처음에는 1)를 저장
for (int i = 0; i < N - 1; i++) {
pq.add(new Path(rest[i + 1] - rest[i], 1));
}
pq.add(new Path(rest[0], 1));
pq.add(new Path(L - rest[N - 1], 1));
} else {
pq.add(new Path(L, 1));
}
// 가장 길이가 긴 것을 꺼내서 나누는 횟수를 추가해준다
for (int i = 0; i < M; i++) {
Path path = pq.poll();
double dis = path.dis;
int cnt = path.cnt;
pq.add(new Path((dis * cnt) / (cnt + 1), cnt + 1));
}
// 길이 값이 소수점이 달려있으면 a, a+1로 나뉜 것이기 때문에 int형 변환 후 +1 필요
double dis = pq.poll().dis;
int ans = (int) dis;
if (ans == dis) System.out.println(ans);
else System.out.println(ans + 1);
}
}
class Path implements Comparable<Path> {
double dis;
int cnt;
public Path(double dis, int cnt) {
this.dis = dis;
this.cnt = cnt;
}
@Override
public int compareTo(Path p) {
return Double.compare(p.dis, this.dis);
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;
public class Main {
static int N, M, L;
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());
L = Integer.parseInt(st.nextToken());
ArrayList<Integer> list = new ArrayList<>();
list.add(0);
list.add(L);
if (N != 0) {
st = new StringTokenizer(br.readLine());
for (int i = 0; i < N; i++) {
list.add(Integer.parseInt(st.nextToken()));
}
Collections.sort(list);
}
int[] rest = list.stream().mapToInt(i -> i).toArray();
// m을 찾기위해 이분탐색
// left가 1부터 시작인 이유는 right만 계속 줄어들어서 mid가 0이 되는 경우가 발생할 수 있기 때문이다.
int left = 1, right = L;
while (left <= right) {
int mid = (left + right) / 2;
// m보다 클경우
if (getCnt(mid, rest) > M) {
left = mid + 1;
}
// m이하일 경우
else {
right = mid - 1;
}
}
System.out.println(left);
}
public static int getCnt(int mid, int[] rest) {
int count = 0;
for (int i = 0; i < rest.length - 1; i++) {
count += (rest[i + 1] - rest[i] - 1) / mid;
}
return count;
}
}
이분 탐색의 원리는 굉장히 쉽지만 이분탐색 문제인지 판별하고 실제로 적용하는 것이 굉장히 어려운 것 같다. 특히 while문 조건과 left, right의 갱신부분은 문제마다 미세하게 달라서 신경을 잘 써야겠다.