https://www.acmicpc.net/problem/1654
집에서 시간을 보내던 오영식은 박성원의 부름을 받고 급히 달려왔다. 박성원이 캠프 때 쓸 N개의 랜선을 만들어야 하는데 너무 바빠서 영식이에게 도움을 청했다.
이미 오영식은 자체적으로 K개의 랜선을 가지고 있다. 그러나 K개의 랜선은 길이가 제각각이다. 박성원은 랜선을 모두 N개의 같은 길이의 랜선으로 만들고 싶었기 때문에 K개의 랜선을 잘라서 만들어야 한다. 예를 들어 300cm 짜리 랜선에서 140cm 짜리 랜선을 두 개 잘라내면 20cm는 버려야 한다. (이미 자른 랜선은 붙일 수 없다.)
편의를 위해 랜선을 자르거나 만들 때 손실되는 길이는 없다고 가정하며, 기존의 K개의 랜선으로 N개의 랜선을 만들 수 없는 경우는 없다고 가정하자. 그리고 자를 때는 항상 센티미터 단위로 정수길이만큼 자른다고 가정하자. N개보다 많이 만드는 것도 N개를 만드는 것에 포함된다. 이때 만들 수 있는 최대 랜선의 길이를 구하는 프로그램을 작성하시오.
<입력>
첫째 줄에는 오영식이 이미 가지고 있는 랜선의 개수 K, 그리고 필요한 랜선의 개수 N이 입력된다. K는 1이상 10,000이하의 정수이고, N은 1이상 1,000,000이하의 정수이다. 그리고 항상 K ≦ N 이다. 그 후 K줄에 걸쳐 이미 가지고 있는 각 랜선의 길이가 센티미터 단위의 정수로 입력된다. 랜선의 길이는 231-1보다 작거나 같은 자연수이다.
- 이분 탐색 알고리즘으로 나눠보면 될 것 같기도 하다. (시간 초과 이슈...)
- 랜선 길이가 아주아주 크기 때문에 long 타입에 신경쓰자.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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 K = Integer.parseInt(st.nextToken()); //랜선의 개수
int N = Integer.parseInt(st.nextToken()); //필요한 랜선 개수
long[] lines = new long[K]; //랜선
long max = 0;
for(int k = 0; k < K; k++) {
lines[k] = Integer.parseInt(br.readLine());
max = Math.max(max, lines[k]);
}
long min = 1; //최소는 1
while(min <= max) {
long mid = (max+min) / 2;
long sum = 0;
for(long line : lines) {
sum += line / mid; //개수만큼
}
if(sum < N) {
max = mid - 1; //짧게 만들어야 함
} else {
min = mid + 1;
}
}
System.out.println((max+min) / 2);
}
}
Binary Search라고 부르는 이진 탐색(이분 탐색) 알고리즘은 정렬되어 있는 리스트에서 탐색 범위를 절반씩 좁혀가면서 데이터를 탐색하는 방법이다. 배열 내부 데이터가 정렬되어 있어야지만 사용할 수 있다.
완전 탐색(순차 탐색)보다 훨씬 시간 복잡도가 낮다. 정렬되어 있을 때, 구하려는 값과 중간값의 비교를 통해 그 이하, 그 이상은 안 봐도 된다는 가정 하에 범위를 줄여나가는 것이기 때문이다.
절반씩 줄여서 탐색하는 과정이라, 순차 탐색의 경우 시간 복잡도가 O(n)인 반면, 이분 탐색은 O(log n)이 된다.

(출처: @suk13574의 벨로그)
크게 두 가지 방법으로 직접 구현할 수 있다. (메서드 binarySearch가 있긴 하지만, 직접 구현하는 법을 알아 두는 것이 좋다고...)
int[] arr; //정렬된 배열
int min = 0;
int max = arr.length - 1;
int mid = 0;
int n; //구해야 하는 숫자
while(min <= max) {
mid = (min + max) / 2;
if(arr[mid] < n) min = mid + 1; //오른쪽 탐색필요
else if(arr[mid] > n) max = mid - 1; //왼쪽 탐색 필요
else //여기가 찾는 곳
}
int[] arr; //정렬된 배열
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;
int n; //구해야 하는 숫자
if(arr[mid] < n) binarySearch(arr, n, mid+1, max); //오른쪽 탐색필요
else if(arr[mid] > n) binarySearch(arr, n, min, mid - 1); //왼쪽 탐색 필요
else //여기가 찾는 곳