[ 알고리즘 ] 백준 2805번: 나무 자르기

이주 weekwith.me·2022년 8월 10일
0

알고리즘

목록 보기
55/73
post-thumbnail

블로그를 이전 중이라 완료되기 전까지는 벨로그에 작성할 계획입니다.
이후 모든 글은 https://weekwith.me 에 작성 예정이니 다른 글이 궁금하시다면 해당 링크를 통해 방문해주세요.

본 글은 [ 백준 ] 2805번: 나무 자르기를 풀고 작성한 글입니다.

문제

설명

상근이는 나무 M미터가 필요하다. 근처에 나무를 구입할 곳이 모두 망해버렸기 때문에, 정부에 벌목 허가를 요청했다. 정부는 상근이네 집 근처의 나무 한 줄에 대한 벌목 허가를 내주었고, 상근이는 새로 구입한 목재절단기를 이용해서 나무를 구할것이다.

목재절단기는 다음과 같이 동작한다. 먼저, 상근이는 절단기에 높이 H를 지정해야 한다. 높이를 지정하면 톱날이 땅으로부터 H미터 위로 올라간다. 그 다음, 한 줄에 연속해있는 나무를 모두 절단해버린다. 따라서, 높이가 H보다 큰 나무는 H 위의 부분이 잘릴 것이고, 낮은 나무는 잘리지 않을 것이다. 예를 들어, 한 줄에 연속해있는 나무의 높이가 20, 15, 10, 17이라고 하자. 상근이가 높이를 15로 지정했다면, 나무를 자른 뒤의 높이는 15, 15, 10, 15가 될 것이고, 상근이는 길이가 5인 나무와 2인 나무를 들고 집에 갈 것이다. (총 7미터를 집에 들고 간다) 절단기에 설정할 수 있는 높이는 양의 정수 또는 0이다.

상근이는 환경에 매우 관심이 많기 때문에, 나무를 필요한 만큼만 집으로 가져가려고 한다. 이때, 적어도 M미터의 나무를 집에 가져가기 위해서 절단기에 설정할 수 있는 높이의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 나무의 수 N과 상근이가 집으로 가져가려고 하는 나무의 길이 M이 주어진다. (1 ≤ N ≤ 1,000,000, 1 ≤ M ≤ 2,000,000,000)

둘째 줄에는 나무의 높이가 주어진다. 나무의 높이의 합은 항상 M보다 크거나 같기 때문에, 상근이는 집에 필요한 나무를 항상 가져갈 수 있다. 높이는 1,000,000,000보다 작거나 같은 양의 정수 또는 0이다.

출력

적어도 M미터의 나무를 집에 가져가기 위해서 절단기에 설정할 수 있는 높이의 최댓값을 출력한다.

풀이

접근법

이진 탐색을 통해 문제를 해결할 수 있다.

시작 값의 경우 0, 끝 값의 경우 주어진 나무의 높이 중 가장 긴 나무로 한다.

이때 반복문을 돌면서 대상이 되는 나무의 길이보다 길이가 긴 나무들만 해당 대상 만큼 자른 합산을 구하여 집에 가져가려는 나무의 높이와 비교해야 한다.

또한 비교의 대상이 되는 높이가 집에 가져가려는 나무보다 긴 경우 너 길게 나무를 잘라도 되기 때문에 시작 값을 자르려 하는 나무의 높이보다 길이가 1만큼 길게 재할당한다.

그리고 해당 대상이 만약 이전 값보다 큰 경우 재할당하여 최댓값을 구할 수 있다.

나의 풀이

접근법을 토대로 문제를 풀면 아래와 같다.

N, M = map(int, input().split())
trees = list(map(int, input().split()))

answer = 0
max_heigth = max(trees)
left, right, target = 0, max_heigth, max_heigth // 2
while left <= right:
    total = sum([ tree - target for tree in trees if tree > target ])

    if total >= M:
        if answer < target:
            answer = target
        left = target + 1

    else:
        right = target - 1

    target = (left + right) // 2            

print(answer)

다른 풀이

아래와 같이 똑같은 이진 탐색이지만 조금 다르게 접근해서 문제를 해결할 수도 있다.

끝 값을 가장 길이가 긴 나무를 M을 N으로 나눈 몫만큼 나눈 것으로 할당하면 된다.

결국 M의 높이 만큼을 집에 가져가기 위해 N개의 나무를 나눠서 가져가야하므로 이러한 계산을 통해 끝 값을 구할 수 있는 것이다.

N, M = map(int, input().split())
trees: list[int] = list(map(int, input().split()))

start, end = 0, max(trees) - (M // N)
while start <= end:
    middle: int = (start + end) // 2
    target: int = sum([tree - middle for tree in trees if tree > middle])

    if target >= M:
        start = middle + 1

    else:
        end = middle - 1

print(end)

Big-O

이진 탐색을 통해 O(logN)의 시간 복잡도가 필요한데 추가적으로 내부에서 N의 길이 만큼 배열을 돌며 잘린 나무의 합산을 계산해야 하기 때문에 최종적으로 시간 복잡도는 O(NlogN)이다.

profile
Be Happy 😆

0개의 댓글