문제: BOJ 15249 - Building Bridges
이 문제는 “기둥 일부를 선택해 1번~n번을 직선 구간으로 연결”하는데, 구간 비용은 높이 차 제곱, 선택되지 않은 기둥은 제거 비용 \(w_i\) 를 지불하는 최적화 문제다.
핵심은 DP를 세운 뒤, 전이를 \(\min (m x + b)\) 꼴로 정리해 Li Chao Tree로 빠르게 처리하는 것이다.
문제 정보
문제 요약:
- 높이가 \(h_i\) 인 기둥 \(n\)개가 1번부터 n번까지 일렬로 있다.
- 일부 기둥을 선택해, 선택된 기둥의 “윗부분”을 구간으로 연결해 다리를 만든다.
- 반드시 1번 기둥과 n번 기둥은 선택해야 한다.
- 선택된 기둥 \(i < j\)를 직접 연결하는 구간 비용은 \((h_i - h_j)^2\).
- 선택되지 않은 기둥은 제거해야 하며, 기둥 \(i\) 제거 비용은 \(w_i\) (음수 가능).
- 총 비용(구간 비용 합 + 제거 비용 합)의 최솟값을 구한다.
제한 조건:
- 시간 제한: 3초
- 메모리 제한: 128MB
- \(2 \le n \le 10^5\)
- \(0 \le h_i \le 10^6\)
- \(0 \le |w_i| \le 10^6\)
입출력 예제
입력 1:
| |
출력 1:
| |
아이디어 요약
- 마지막으로 선택한 기둥 인덱스를 상태로 잡으면, 다음으로 선택할 기둥 \(j\)를 정할 때 **사이에 있는 기둥은 전부 “미선택 → 제거”**가 된다.
- 따라서 전이에서 “사이에 있는 제거 비용”은 구간 합(누적합)으로 한 번에 더할 수 있다.
- \((h_i-h_j)^2 = h_j^2 - 2 h_i h_j + h_i^2\) 로 펼치면, \(j\)에 대해 \(\min (m x + b)\) 형태가 되고 Li Chao Tree로 \(O(\log H)\)에 처리된다.
접근 방식
DP 정의
기둥 \(j\)가 선택되었고, 그때까지의 최소 비용을 \(dp[j]\)라고 하자. (반드시 \(dp[1]=0\))
누적합 \(S[k] = \sum_{t=1}^{k} w_t\) 를 두면, 마지막 선택이 \(i\)에서 \(j\)로 바로 이어질 때:
- 구간 비용: \((h_i - h_j)^2\)
- 중간 제거 비용: \(\sum_{t=i+1}^{j-1} w_t = S[j-1] - S[i]\)
따라서 점화식은:
\[ dp[j] = \min_{1 \le i < j}\left(dp[i] + (h_i-h_j)^2 + (S[j-1]-S[i])\right) \]이를 정리하면:
\[ dp[j] = S[j-1] + h_j^2 + \min_{i- \(m = -2h_i\)
- \(b = dp[i] - S[i] + h_i^2\)
- \(x = h_j\)
이 직선들의 최솟값을 빠르게 질의하면 된다.
알고리즘 설계 (Mermaid)
flowchart TD
A["입력: n, 배열 h[1..n], w[1..n]"] --> B["누적합 S 계산"]
B --> C["dp[1] = 0"]
C --> D["Li Chao Tree 초기화(x 범위: 0..max(h))"]
D --> E["i = 1 직선 추가m = -2*h1, b = dp1 - S1 + h1^2"]
E --> F["j = 2..n 반복"]
F --> G["best = Li Chao query(x = h[j])"]
G --> H["dp[j] = S[j-1] + h[j]^2 + best"]
H --> I["직선 추가m = -2*h[j], b = dp[j] - S[j] + h[j]^2"]
I --> F
H --> J["정답: dp[n] 출력"]
복잡도 분석
| 항목 | 복잡도 | 비고 |
|---|---|---|
| 전이(각 j) | \(O(\log H)\) | Li Chao 삽입/질의 |
| 전체 시간 복잡도 | \(O(n \log H)\) | \(H = \max h_i \le 10^6\) |
| 공간 복잡도 | \(O(n)\) | 배열 + Li Chao 노드(동적) |
C++ 구현 코드
| |
코너 케이스 및 실수 포인트
| 케이스 | 설명 | 처리 |
|---|---|---|
| \(w_i\) 음수 | 제거하면 오히려 이득 | DP에 그대로 포함되므로 별도 처리 불필요 |
| 오버플로우 | \((h_i-h_j)^2\) 및 dp가 큼 | long long + 비교는 __int128 사용 |
| 높이 중복 | 같은 \(h\)가 여러 번 등장 | Li Chao는 중복 기울기/질의 모두 처리 가능 |
| \(h_i=0\) | x 범위 하단 | Li Chao의 x 범위를 0부터 포함 |
![Featured image of post [Algorithm] C++ 백준 15249번: Building Bridges](/post/algorithm/2025-12-19-boj-15249-building-bridges-cpp-solution/wordcloud_hu_a28ff179e77ef879.png)
![[Algorithm] C++ 백준 14853번: 동전 던지기](/post/algorithm/2025-12-19-boj-14853-coin-tossing-cpp-solution/wordcloud_hu_38e38de527bab2db.png)
![[Algorithm] C++ 백준 14899번: 수열과 쿼리 19](/post/algorithm/2025-12-19-boj-14899-sequence-and-queries-19-segtree-beats-cpp-solution/wordcloud_hu_b29f404e994b6fb2.png)
![[Algorithm] C++ 백준 15249번: Building Bridges](/post/algorithm/2025-12-19-boj-15249-building-bridges-cpp-solution/wordcloud_hu_74e7f50c7caada4c.png)
![[Algorithm] C++ 백준 16745번: What Goes Up Must Come Down](/post/algorithm/2025-12-19-boj-16745-what-goes-up-must-come-down-cpp-solution/wordcloud_hu_8b312f25f2104ffa.png)
![[Algorithm] C++ 백준 16895번: 님 게임 3](/post/algorithm/2025-12-19-boj-16895-nim-game-3-cpp-solution/wordcloud_hu_f071b137bed4b6a4.png)
![[Algorithm] C++ 백준 5498번: Batch Scheduling](/post/algorithm/2025-12-19-boj-5498-batch-scheduling-cpp-solution/wordcloud_hu_ea128457d0ff4d1c.png)
![[Algorithm] C++ 백준 2586번: 소방차](/post/algorithm/2025-12-19-boj-2586-firetruck-cpp-solution/wordcloud_hu_c7df555f7b0ecd9e.png)
![[Algorithm] C++ 백준 20176번: Needle](/post/algorithm/2025-12-19-boj-20176-needle-cpp-solution/wordcloud_hu_d0eac9b197bd7f0d.png)
![[Algorithm] C++ 백준 7727번: Byephone](/post/algorithm/2025-12-19-boj-7727-byephone-hirschberg-lcs-cpp-solution/wordcloud_hu_7264c733e5d43dbc.png)
![[Algorithm] C++ 백준 22878번: 간단한 문제](/post/algorithm/2025-12-19-boj-22878-simple-problem-cpp-solution/wordcloud_hu_9cd053bc1be8114a.png)