문제
집에서 시간을 보내던 오영식은 박성원의 부름을 받고 급히 달려왔다. 박성원이 캠프 때 쓸 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보다 작거나 같은 자연수이다.
출력
첫째 줄에 N개를 만들 수 있는 랜선의 최대 길이를 센티미터 단위의 정수로 출력한다.
예제 입력 1
4 11
802
743
457
539
예제 출력 1
200
힌트
802cm 랜선에서 4개, 743cm 랜선에서 3개, 457cm 랜선에서 2개, 539cm 랜선에서 2개를 잘라내 모두 11개를 만들 수 있다.
📝 제출 답안
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());
long n = Long.parseLong(st.nextToken());
int[] arr = new int[k];
long max = 0;
for (int i = 0; i < k; i++) {
arr[i] = Integer.parseInt(br.readLine());
if (arr[i] > max) {
max = arr[i];
}
}
max++;
long min = 0;
long mid = 0;
while (min < max) {
mid = (min + max) / 2;
long count = 0;
for (int i = 0; i < arr.length; i++) {
count += (arr[i]/mid);
}
if (count < n) {
max = mid;
} else {
min = mid + 1;
}
}
System.out.println(min - 1);
}
}
이 문제는 이분 탐색 알고리즘을 사용하는 문제임을 힌트로 얻었다.
k는 주어진 랜선의 개수, n은 만들어야 할 랜선의 개수를 입력 받는다.
arr 정수형 배열에 각 랜선의 길이를 저장하고, max에는 가장 긴 랜선의 길이를 저장한다. max 값은 이분 탐색에서 초기 상한값으로 사용하기 위해서 max+1로 설정한다.
min은 이분 탐색의 하한값, max는 이분 탐색의 상한값, mid는 이분 탐색에서 중간값(현재 탐색 중인 랜선의 길이)을 나타낸다.
각 랜선의 길이 arr[i]를 mid로 나눈 몫을 모두 더해서 현재 mid 길이로 잘랐을 때 만들 수 있는 랜선의 개수 count를 구한다. 마지막으로 min이 최소값을 초과할 때 종료되므로 정답은 min-1을 출력한다.
🪄 이분탐색 알고리즘
이분 탐색(Binary Search) : 정렬된 데이터에서 특정 값을 빠르게 찾기 위해 사용하는 알고리즘이다. 데이터를 절반씩 나누어 검색 범위를 좁혀 나가기 때문에 효율적이고, 이 알고리즘의 시간 복잡도는 O(log n)으로 매우 빠른 편이다.
low : 검색 범위의 시작점
high : 검색 범위의 끝점
mid : 범위의 중간값 mid = (low+high)/2
low <= high가 유지되는 동안 검색 범위를 줄이며 계속 반복하여 값을 비교하여 target == mid 이면 값을 찾아 탐색이 종료된다.
장점
- 검색 시간 복잡도가 O(log n)으로, 큰 데이터셋에서도 빠르게 동작한다는 효율성이 있다.
- 알고리즘이 간단하여 코딩이 쉽다.
단, 제한 사항으로는 데이터가 정렬된 상태여야 사용 가능하다는 것이다.
하지만 위 문제를 해결할 때 따로 정렬을 하지 않았던 이유는
탐색의 대상이 랜선의 길이라는 값의 범위이기 때문이다.
랜선의 길이 값의 범위는 0부터 max까지 이다. 그러므로 배열의 요소들이 정렬될 필요는 없고, 탐색하려는 길이의 범위는 자연스럽게 정렬된 상태로 생각할 수 있다.
'백엔드개발 > Baekjoon' 카테고리의 다른 글
[Baekjoon] 2839 설탕 배달 - JAVA (0) | 2024.12.26 |
---|---|
[Baekjoon] 11651 좌표 정렬하기 2 - JAVA (1) | 2024.11.29 |
[Baekjoon] 1966 프린터큐 - JAVA (0) | 2024.11.21 |
[Baekjoon] 10773 제로 - JAVA (2) | 2024.11.21 |
[Baekjoon] 1676 팩토리얼 0의 개수 - JAVA (0) | 2024.11.18 |