코딩테스트/백준

[Python] 백준 2805. 나무자르기

jungmin.park 2023. 11. 1. 16:02

https://www.acmicpc.net/problem/2805

 

2805번: 나무 자르기

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

www.acmicpc.net

문제 설명:

상근이가 얻으려는 나무의 길이 M , 나무의 수 N

M을 얻기 위해 절단기로 자를 수 있느 나무의 최대 높이값을 구하는 문제

만약 4개의 나무 [20, 15, 10, 17]이 주어질때 7미터 얻으려고 할때 15미터로 설정하고 자르면 [0,0,5,2] 7미터를 얻을 수 있다.

 

이진탐색으로 풀 수 있지만 조금 방법을 바꿔보고 싶었다.

  • 나무의 리스트를 역순으로 정렬하고 첫번째 요소를 절단기의 최대높이(limit)라고 가정한다.
  • 최대높이로 잘랐을때 절단된 나무길이의 합을 구하고 원하는 길이보다 합이 작으면 요소를 앞으로 옮겨준다.
  • 그걸 반복하다보면 절단된 나무길이의 합이 구하고자 하는 길이의 합보다 커질 것이다.
  • 이때 그 요소와 앞의 요소를 보내 이진탐색을 시작한다.
def func(lst):
    p = 0
    while True:
        limit = lst[p]
        gather = 0
        for val in lst:
            if limit < val:
                gather += val - limit
        if gather < M:
            p += 1
        elif gather == M:
            return lst[p]
        else:
            return binary_search(lst[p-1], lst[p])

하지만 실패했다..! ㅠ 아마 요소를 더했을때 아예 인덱스를 벋어나는 경우도 있을 것이다.

 

다시 이진탐색으로만 찾아보자

  • start = 1로 end 는 주어진 트리 리스트에서 가장 큰 값으로 하자
tree = [4,42,40,26,46]
start, end = 1, max(tree)

 

  • mid는 start와 end의 중간값으로 한다.
  • 트리의 길이의 합을 구하기 위한 변수도 초기화 해준다.
    mid = (start+end) // 2
    tree_length = 0

 

  • 절단기의 최대 높이(mid)를 지정해주었으니 나무길이에서 잘라서 몇개가 나오는지 세어본다.
  • 이때 주의점은 최대높이보다 작은것은 잘리지 않으니 절단기 최대높이보다 큰 것만 잘라야 한다.
    for i in tree:
        if mid < i:
            tree_length += i - mid

 

  • 잘린 트리의 합이 크면 너무 많이 잘랐기 때문에 최대높이를 높여 다시 반복하도록 한다.
  • 합이 작으면 최대높이를 낮춰줘서 다시 반복하도록 한다.
    if tree_length >= M:
        start = mid + 1
    else:
        end = mid - 1
  
  print(end)