[알고리즘] 랜선자르기 (결정알고리즘)

(문제) 랜선자르기 (결정알고리즘)


엘리트 학원은 자체적으로 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줄에 걸쳐 이미 가지고 있는 각 랜선의 길이가 센티미터 단위의 2^31 - 1 이하의 자연수로 주어진다.

출력설명

첫째 줄에 N개를 만들 수 있는 랜선의 최대 길이를 센티미터 단위의 정수로 출력한다.

테스트케이스

입력예제 출력예제
4 11
802
743
457
539
200
5 10
273
401
753
345
105
150

해결방법

우선 문제 해결을 위해서 무엇을 구해야하는지 바라보자.

가지고 있는 랜선을 N개 만큼 가장 긴 cm 로 잘라야한다.

우리는 먼저 랜선을 N개 만큼 가질 수 있도록 나눠야한다.

이 방법은 첫번째 부터 순회를 하면서 구해도 괜찮다.

즉, 1 부터 시작해서 현재 가지고 있는 랜선 중에서 가장 길이가 긴 랜선 까지 반복문을 돌린다.

그래서 그 값들 중에서 현재 가지고 있는 랜선을 각각 1~가장길이간긴랜선 길이 만큼 차례로 나눠보고, 나눠서 나온 몫을 이용하여 N개를 구하면 그 cm를 찾은 것이다.

하지만 첫번째 부터 순회를 하는 방식은 속도 측면으로 너무 느리다.

그래서 우리는 이분탐색을 통해서 그 값을 찾는 것이다.

1 ~ 가장 큰 cm 의 중앙을 구해서 그 중앙의 값을 구한 것을 가지고, 가지고 있는 랜선들의 길이를 나눠서 그 나눈 몫이 N개 가 되는지 확인을 한다.

만약에 N개 보다 작으면 cm 값을 줄이고, N개의 값이 더 크면 cm 값을 높인다.
(나오는 값이 N개 보다 작으면 큰 cm를 이용하여 나눴기 때문에 N 보다 작아서 작으면 cm 값을 줄이는 것이다.)

줄이는 방법은 이분 탐색을 통해 LT와 RT의 위치를 바꾸면서 줄인다.

이분 탐색 공부용 주소


코드

n, m = map(int, input().split())

arr = [int(input()) for _ in range(n)]

# 최대 max(arr)
# 최소 0
arr.sort()

LT = 0
RT = arr[-1]

save = 0

while LT <= RT:
    # 중앙을 고른다.
    MID = (LT + RT) // 2
    s_line = sum(map(lambda x: x//MID, arr))
          
    # 고른 것이 만약 11 보다 클 경우
    if s_line < m:
        RT = MID - 1
    elif s_line >= m:
        # 동일한 개수에 도달 햇을 때, 더큰 길이를 알기 위함
        if save < MID:
            # 기존 최적 저장
            LT = MID + 1
            save = MID
        LT = MID + 1
        
print(save)

댓글남기기