PS(Problem Solving)/BOJ

[백준/BOJ] 2805번: 나무 자르기

JunsuKim 2022. 1. 20.
728x90

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

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

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

해설

이분탐색을 통해 문제를 푼다.

예제2를 봐보자.

나무의 길이가 제일 긴 46을 max로 두고 min은 0이라 하자.

이 때 중간값은 23이 된다.

이제 중간값인 23m부터 나무를 잘라보자.

23m가 안되는 나무는 자를 것이 없으므로 0m의 나무를 얻을 수 있다.

윗부분이 합을 보면 62m가 된다. 이는 상근이가 필요한 20m보다 한참 큰 값이므로 더 잘라야 하는 기준을 높여야 한다.

따라서 min을 23에 1을 더한 값인 24로 잡고 다시 중간값을 구하면 46m와 24m의 중간이므로35m가 된다.

35m를 기준으로 나무를 잘라보자.

자른 나무들의 합을 구해보면 23m가 되므로 여전히 가지고자 하던 길이보다 길다.

이번엔 46과 36의 중간 값인 41m를 보자.

자른 나무의 길이의 합을 보면 6m로 가지려던 나무의 길이보다 한참 부족하다.

따라서 하한선(min)은 놔두고 상한선(max)를 41로 정해준다.

이제 41과 36의 중간값인 38m(소수점 버림)로 나무를 자른다.

이 또한 길이의 합이 20이 되지 않으므로 상한가를 38로 내리자.

이제 38과 36의 중간인 37m로 나무를 자른다.

남은 나무의 길이의 합은 17m이다. 상근이가 갖고자 하는 길이에 부족하다.

따라서 상한선을 낮춰 37m와 36m의 중간 값인 36m(소수점 버림)로 잘라야 한다.

이제 20m가 딱 맞아떨어진다.

이렇게 원하는 길이(m)과 남은 나무의 길이가 같다면 하한선을 높여 36 + 1이 된다.

(if문을 원하는 길이보다 남은 길이의 합이 작다면 상한가를 고치고 나머지는 하한가 + 1로 만들었기 때문이다.)

이제 하한선이 상한선보다 커지므로 이분탐색을 종료한다.

따라서 실제 길이보다 +1이 더 됬으므로 출력을 할 때 -1을 하여 출력해주면 된다.

소스 코드

import java.util.*

fun main() {
    var st = StringTokenizer(readLine())
    val n = st.nextToken().toInt()
    val m = st.nextToken().toInt()
    val tree = Array(n) { 0 }
    st = StringTokenizer(readLine())
    var max = 0
    var min = 0
    for(i in 0 until n ){
        tree[i] = st.nextToken().toInt()
        if(max < tree[i]) max = tree[i]
    }
    while(min < max) {
        var mid = (max + min) / 2
        var sum: Long = 0
        for(i in tree.indices) {
            if(tree[i] - mid > 0) sum += tree[i] - mid
        }
        if(sum < m) max = mid
        else min = mid + 1
    }
    println(min - 1)
}
728x90

'PS(Problem Solving) > BOJ' 카테고리의 다른 글

[백준/BOJ] 15652번: N과 M(4)  (0) 2022.01.21
[백준/BOJ] 15651번: N과 M(3)  (0) 2022.01.21
[백준/BOJ] 14501번: 퇴사  (0) 2022.01.20
[백준/BOJ] 1874번: 스택 수열  (0) 2022.01.18
[백준/BOJ] 2×n 타일링 2  (0) 2022.01.16

댓글