본문 바로가기
알고리즘 풀이

백준 | 2805번 나무 자르기 (JavaScript 풀이)

by sseul-log 2025. 10. 4.

이분 탐색(Binary Search) & 매개변수 탐색

이분 탐색이란?

정의: 정렬된 데이터에서 탐색 범위를 절반씩 줄여가며 원하는 값을 찾는 알고리즘

특징: 매번 탐색 범위가 1/2로 감소 → O(log N)의 시간복잡도

핵심: "답의 범위를 좁혀가며 최적값 찾기"


 매개변수 탐색이란?

일반적인 문제: "조건을 만족하는 X를 구하시오"
     ↓ (발상의 전환)
매개변수 탐색: "X가 주어졌을 때 조건을 만족하는가?"
     ↓ (이분 탐색 적용)
답: 조건을 만족하는 X의 최댓값/최솟값

 


📌 문제 분석

문제 요약

나무꾼이 나무를 자르는 문제. 절단기 높이 H를 설정하면 H보다 높은 나무들이 잘려요!

상황 설명

• 절단기 높이 H 설정 • H보다 높은 나무의 윗부분만 잘림 • 잘린 나무들을 모두 가져감 • 최소 M미터가 필요

목표

M미터 이상을 얻으면서 H를 최대한 높게 설정

제한사항

• N: 나무의 수 (1 ≤ N ≤ 1,000,000) • M: 필요한 나무 길이 (1 ≤ M ≤ 2,000,000,000) • 각 나무의 높이 (0 ≤ 높이 ≤ 1,000,000,000)

 

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

 


 

문제 접근 방법

1️⃣ 그림으로 이해하기

나무 높이: [20, 15, 10, 17]
절단기 H = 15

    20      15      10      17
    ||      ||      ||      ||
    ||      ||      ||      ||
    ||      ==      ||      ||  ← H=15 (절단선)
    ||              ||      ==
    ||                      
   [5m]    [0m]    [0m]    [2m]  = 총 7m 획득!

 


2️⃣ 왜 이분 탐색을 사용할까?

❌ 완전탐색 시도

H를 0부터 최대높이까지 하나씩 시도

  • 시간복잡도: O(최대높이 × N) = O(10⁹ × 10⁶) = 시간초과!

✅ 이분 탐색 적용

H의 범위를 절반씩 줄여가며 탐색

  • 시간복잡도: O(log(최대높이) × N) = O(30 × 10⁶) = 통과!

3️⃣ 이분 탐색 시뮬레이션

나무: [4, 42, 40, 26, 46], M=20 필요

1단계) left=0, right=46, mid=23
       → 잘린 나무: 19+17+3+23=62 ≥ 20 ✓
       → 더 높일 수 있나? left=24

2단계) left=24, right=46, mid=35
       → 잘린 나무: 7+5+11=23 ≥ 20 ✓
       → 더 높일 수 있나? left=36

3단계) left=36, right=46, mid=41
       → 잘린 나무: 1+5=6 < 20 ✗
       → 높이 낮춰야 함! right=40

4단계) left=36, right=40, mid=38
       → 잘린 나무: 4+2+8=14 < 20 ✗
       → 높이 낮춰야 함! right=37

5단계) left=36, right=37, mid=36
       → 잘린 나무: 6+4+10=20 = 20 ✓
       → 더 높일 수 있나? left=37

6단계) left=37, right=37, mid=37
       → 잘린 나무: 5+3+9=17 < 20 ✗
       → 종료!

답: H=36

 

 


📝 풀이 코드

JavaScript 코드

// 언어 : Javascript , (성공/실패) : 1/0 , 메모리 : 137416 KB , 시간 : 676 ms

const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const [N, M] = input[0].split(" ").map(Number);
const trees = input[1].split(" ").map(Number);

// 이진 탐색 범위 설정
let left = 0;                    // 최소 높이
let right = Math.max(...trees);  // 최대 높이 (가장 높은 나무)
let answer = 0;                   // 정답 저장

while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    
    // mid 높이로 잘랐을 때 얻는 나무 계산
    let total = 0;
    for (const tree of trees) {
        if (tree > mid) {
            total += tree - mid;
        }
    }
    
    // M미터 이상 얻을 수 있는지 확인
    if (total >= M) {
        answer = mid;      // 가능하면 답 갱신
        left = mid + 1;    // 더 높은 높이 시도
    } else {
        right = mid - 1;   // 높이를 낮춰야 함
    }
}

console.log(answer);

 


 

🔎 코드 상세 해설

Step 1. 탐색 범위 설정

let left = 0;                    // 절단기 최소 높이
let right = Math.max(...trees);  // 절단기 최대 높이
  • 최소: 0 (모든 나무를 다 자르는 경우)
  • 최대: 가장 높은 나무의 높이 (아무것도 안 자르는 경우)

Step 2. 중간값으로 시뮬레이션

let total = 0;
for (const tree of trees) {
    if (tree > mid) {         // mid보다 높은 나무만
        total += tree - mid;  // 잘린 부분 계산
    }
}
  • 절단기보다 낮은 나무는 안 잘림 → 0
  • 절단기보다 높은 나무만 계산

Step 3. 범위 조정 로직

if (total >= M) {         // 충분히 얻었다면
    answer = mid;         // 현재 높이 저장
    left = mid + 1;       // 더 높게 시도
} else {                  // 부족하다면
    right = mid - 1;      // 더 낮게 시도
}

 

⚠️ 중요: total >= M일 때도 계속 탐색! (최댓값을 구해야 하므로)


⏱️ 시간복잡도 분석

항목 복잡도 설명
이분 탐색 횟수 O(log H) 약 30번
각 탐색마다 계산 O(N) 1,000,000
전체 O(N × log H) 약 30,000,000

 


 

⚠️ 실수하기 쉬운 부분

❌ 자주 하는 실수 TOP 3

1. answer 업데이트 빼먹기

// 잘못된 코드
if (total >= M) {
    left = mid + 1;  // answer = mid 빼먹음!
}

 

2. 범위 설정 실수

// 잘못된 코드
let right = 1000000000;  // 나무 최대 높이가 아님!

3. 오버플로우 주의

// M이 20억까지 가능 → Number 타입으로 충분 (2^53까지 안전)

 


이분 탐색 마스터하기

이런 키워드 보면 이분 탐색!

✅ "최댓값/최솟값을 구하시오"

✅ "~이상/이하를 만족하는"

✅ 답의 범위가 정해져 있음

✅ 특정 값으로 O(N) 계산 가능

✅ 완전탐색하면 시간초과


유사 문제 패턴

패턴 1. 입국심사 문제

심사관들이 있고, 각자 심사 시간이 다름
→ "T시간 안에" 모든 사람 심사 가능?
→ T를 이분 탐색!

패턴 2. 예산 문제

각 부서별 요청 예산이 있고, 총 예산 한계
→ "상한액 X로" 예산 분배 가능?
→ X를 이분 탐색!

패턴 3. 징검다리 문제

돌들 사이의 거리
→ "최소 거리 D 이상" 유지하며 제거 가능?
→ D를 이분 탐색!

 

핵심 정리

  1. 이분 탐색 = "답을 맞추는 게임"
  2. 매개변수 탐색 = "조건 만족 여부를 O/X로 판단"
  3. left, right 범위 설정이 제일 중요!
  4. answer 변수로 가능한 답을 계속 갱신
  5.  

실전 꿀팁

구분 방법
최댓값 구하기 if (조건 만족) left = mid + 1
최솟값 구하기 if (조건 만족) right = mid - 1
핵심 항상 answer 변수에 가능한 답 저장!

💭 느낀점

  • 처음엔 "높이를 하나씩 다 해봐야 하나?" 했는데 이분 탐색으로 해결!
  • 매개변수 탐색이라는 발상의 전환이 신기했음
  • 비슷한 패턴의 문제가 많아서 한 번 익혀두면 유용할 듯