본문 바로가기
Programming/BOJ

[C/C++] 백준 #2805 나무 자르기(이분 탐색)

by 작은별하나 2024. 8. 16.

이분 탐색은 단순증가하는 형태의 함수에서 원하는 값을 빠르게 찾는데 사용을 합니다.  시간 복잡도는 O(logN)이 됩니다.  하지만, 어떤 지점을 기준으로 실패와 성공이 갈릴 때, 그 기준점을 찾을 때에도 유용합니다.

 

Tree cutting

 

나무 자르기 문제 역시 이분 탐색을 이용하여 성공할 때 최적값을 찾을 수 있습니다.

 

문제의 링크는 다음과 같습니다.

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

 

코드 설명

1. `readint()` 함수

int readint() {
    int s=0,c;
    for(;;){
        c=getchar();
        if(c<'0'||c>'9') break;
        s=s*10+c-'0';
    }
    return s;
}

- 이 함수는 표준 입력으로부터 정수를 읽어오는 함수입니다. 
- `getchar()`를 통해 하나씩 문자를 읽어오고, 이 문자가 숫자일 경우에만 정수로 변환하여 `s`에 누적합니다.
- 숫자가 아닌 문자가 나올 때 루프를 종료하고, 최종적으로 누적된 정수 `s`를 반환합니다.
- 이 함수는 여러 개의 숫자를 효율적으로 읽기 위해 구현되었습니다.

2. `main()` 함수

int main()
{
    int n, m, f=0, l=0;
    static int v[1000000];
    n=readint(); m=readint();
    for(int i=0;i<n;i++) v[i]=readint(), l=(l<v[i])?v[i]:l;

- `n`은 나무의 수, `m`은 필요한 나무의 양입니다.
- `f`는 톱질 높이의 최저값, `l`은 톱질 높이의 최대값입니다. 처음에는 `f`는 0으로 초기화되고, `l`은 나무의 최대 높이로 설정됩니다.
- `v` 배열은 각 나무의 높이를 저장합니다. 최대 1,000,000개의 나무 높이를 저장할 수 있습니다.
- 루프를 통해 `n`개의 나무 높이를 `v` 배열에 저장하고, 이 중 최대값을 찾아 `l`에 저장합니다.

3. 이진 탐색을 통한 최적 톱질 높이 찾기

    while(f < l-1)
    {
        int c=(f+l)/2, i; long long sum=0;
        for(i=0;i<n && sum<m; i++) sum+=(v[i]>c)?v[i]-c:0;
        if(sum>=m) f=c; else l=c;
    }
    printf("%d\n", f);
}

- `while(f < l-1)` 루프는 이진 탐색을 수행하여 톱질할 최적의 높이 `f`를 찾습니다.
- `c`는 `f`와 `l`의 중간값이며, 현재 시도하는 톱질 높이입니다.
- `sum`은 `c` 높이에서 톱질했을 때 얻을 수 있는 나무의 총량입니다.
- `for` 루프는 각 나무에 대해 `v[i]`가 `c`보다 클 경우, 나무의 높이에서 `c`를 뺀 값을 `sum`에 더합니다.
- `sum`이 필요한 양 `m` 이상이 되면, `f`를 `c`로 설정하여 톱질 높이를 높이고, 그렇지 않으면 `l`을 `c`로 설정하여 톱질 높이를 낮춥니다.
- 최종적으로 최적의 톱질 높이 `f`를 출력합니다.

이 코드는 이분 탐색을 사용하여 최적의 톱질 높이를 효율적으로 찾습니다. O(nlogh)의 시간 복잡도를 가지며, `n`은 나무의 수, `h`는 나무의 최대 높이입니다. 이진 탐색을 통해 가능성 있는 톱질 높이를 점진적으로 줄여가며 최적의 해답을 찾아냅니다.

 

제가 작성한 코드입니다.

//------------------------------------------------
//    baekjoon #2805
//        - by Aubrey Choi
//        - created at 2019-09-11
//------------------------------------------------
#include <stdio.h>

int readint() {int s=0,c; for(;;){c=getchar();if(c<'0'||c>'9')break;s=s*10+c-'0';}return s;}
int main()
{
    int n, m, f=0, l=0;
    static int v[1000000];
    n=readint(); m=readint();
    for(int i=0;i<n;i++) v[i]=readint(), l=(l<v[i])?v[i]:l;
    while(f < l-1)
    {
        int c=(f+l)/2, i; long long sum=0;
        for(i=0;i<n && sum<m; i++) sum+=(v[i]>c)?v[i]-c:0;
        if(sum>=m) f=c; else l=c;
    }
    printf("%d\n", f);
}
반응형