문제 정보
- 링크: https://www.acmicpc.net/problem/16783
- 출처: JOI Open Contest 2017
- 시간 제한: 2초
- 메모리 제한: 512MB
문제 요약
JOI Kingdom의 $N$개 채굴 지점이 2차원 평면에 분포합니다. 각 지점은 금(양수 가치)이거나 돌(음수 비용)입니다. 두 개의 평행선을 선택하여 그 사이 영역의 모든 지점을 채굴할 때, 얻은 금의 총 가치 - 버린 돌의 총 비용을 최대화해야 합니다.
입출력 형식
입력:
| |
- $N$ (1 ≤ N ≤ 2000): 지점 개수
- Wᵢ > 0: i번 지점의 금 가치
- Wᵢ < 0: i번 지점의 돌 비용 (절댓값이 실제 비용)
출력:
| |
예제
입력 1:
| |
출력 1:
| |
접근 개요
이 문제의 핵심 관찰은 평행선의 기울기가 정해지면, 모든 점을 기울기에 수직인 방향으로 정렬할 수 있다는 것입니다. 이렇게 정렬된 순서에서 연속된 구간의 합 중 최댓값(Maximum Subarray Sum)을 구하는 문제로 축소됩니다.
핵심 아이디어:
- 기울기 변화에 따라 점의 순서가 어떻게 바뀌는지 추적
- 점 순서가 바뀌는 순간 = 두 점을 잇는 직선의 기울기와 평행할 때
- 따라서 모든 가능한 점 쌍의 기울기를 이벤트로 사용
- 각 기울기 상태에서 최대 부분 합을 세그먼트 트리로 계산
알고리즘 설계
상태 표현
- 점의 순열: 현재 기울기 하에서 점들이 정렬된 순서
- 세그먼트 트리 노드: 각 노드가 4가지 정보 유지
lmax: 왼쪽 경계에서 시작하는 최대 합rmax: 오른쪽 경계에서 끝나는 최대 합sum: 구간 전체 합max_val: 구간 최대 부분 합 (Kadane 알고리즘 원리)
노드 병합 로직
두 구간을 병합할 때 최대 부분 합은 다음 3가지 중 하나:
- 왼쪽 구간의 최대 부분 합
- 오른쪽 구간의 최대 부분 합
- 왼쪽의
rmax+ 오른쪽의lmax(구간 경계를 넘는 경우)
스위핑 알고리즘
| |
복잡도 분석
시간: $O(N^2 \log N)$
- 이벤트 생성: $O(N^2)$
- 이벤트 정렬: $O(N^2 \log N)$
- 스위핑 및 세그먼트 트리 업데이트: $O(N^2 \log N)$
공간: $O(N)$ (세그먼트 트리, 점 배열, 순열 배열)
구현
| |
코너 케이스 체크리스트
- 빈 영역 선택: 어떤 점도 포함하지 않는 평행선 영역 (이익 = 0)
- 단일 지점: N=1일 때 해당 지점만 고려
- 모두 같은 x/y 좌표: 수직/수평 직선만 가능
- 공선점(collinear points): 여러 점이 일직선 위에 있을 때 그룹 처리 필수
- 음수만 존재: 아무것도 채굴하지 않는 것이 최적
- 좌표 범위: $±10^9$로 오버플로우 주의 (long long 사용)
제출 전 점검
- 오버플로우: 교차곱 계산 시 long long 사용
- 세그먼트 트리: merge 함수의 4가지 경우 모두 처리
- 공선점: 직선 방정식으로 그룹화 (단순 min/max 사용 안 됨)
- 초기화: pos[], perm[] 배열 정확히 관리
- 기울기 비교: 정수 범위 내에서 정확한 대소비교
정당성 증명
핵심 불변식: 매 스위핑 단계에서 점의 순열은 실제 기울기 변화를 정확히 반영합니다.
- 기울기 변화의 이산성: 점 순서가 바뀌는 순간은 정확히 두 점을 잇는 직선의 기울기와 평행할 때만 발생
- 공선점 처리의 정확성: 같은 직선 위의 점들은 기울기 변화 순간에 함께 역순 배열되므로, 직선 방정식으로 그룹화하면 정확히 어떤 점들이 바뀔지 파악 가능
- 최대 부분 합의 최적성: 세그먼트 트리의 merge 함수가 Kadane 알고리즘의 원리를 구현하므로, 각 기울기 상태에서 현재 최적해를 올바르게 계산
따라서 모든 기울기 상태를 순회하면서 최댓값을 갱신하면, 전역 최적해를 보장합니다.
![Featured image of post [Algorithm] C++ 백준 16783번: Bulldozer](/post/algorithm/2025-12-04-boj-16783-bulldozer-cpp-solution/wordcloud_hu_2ed5e0efdf7a0e5e.png)
![[Algorithm] C++ 백준 16313번: Janitor Troubles](/post/algorithm/2025-12-04-boj-16313-janitor-troubles-geometry-brahmagupta-cpp-solution/wordcloud_hu_9e3b0424f18fce0b.png)
![[Algorithm] C++ 백준 16746번: Four-Coloring](/post/algorithm/2025-12-04-boj-16746-four-coloring-cpp-solution/wordcloud_hu_4d8a2c47b9c8078f.png)
![[Algorithm] C++ 백준 16783번: Bulldozer](/post/algorithm/2025-12-04-boj-16783-bulldozer-cpp-solution/wordcloud_hu_a602d834fcef58ce.png)
![[Algorithm] C++ 백준 16983번: Coin Collecting](/post/algorithm/2025-12-04-boj-16983-coin-collecting-cpp-solution/wordcloud_hu_59f275e060977e1b.png)
![[Algorithm] C++ 백준 17682번 Tents](/post/algorithm/2025-12-04-boj-17682-tents-combinatorics-cpp-solution/wordcloud_hu_2726d57ba0310c29.png)
![[Algorithm] C++ 백준 7626번: 직사각형](/post/algorithm/2025-09-16-boj-7626-rectangles-union-area-line-sweep-segment-tree-cpp-solution/wordcloud_hu_a4f83acea5a7ea04.png)
![[Algorithm] C++ 백준 11012번: Egg - 2D 직사각형 쿼리 스위핑+BIT](/post/algorithm/2025-08-14-boj-11012-egg-cpp-solution/wordcloud_hu_ff8aab4a98180994.png)
![[Algorithm] C++ 백준 10076번: 휴가 - 최적 풀이](/post/algorithm/2025-08-14-boj-10076-holiday-ioi-2014-cpp-solution/wordcloud_hu_b2d0e7fda43f1a4a.png)
![[Algorithm] C++ 백준 12918번: 정리정돈](/post/algorithm/2025-08-14-boj-12918-cleaning-up-mirror-symmetry-hungarian-cpp-solution/wordcloud_hu_992993152683211.png)
![[Algorithm] C++ 백준 13323번: BOJ 수열 1 - Slope Trick](/post/algorithm/2025-08-14-boj-13323-boj-sequence-1-slope-trick-pq-cpp-solution/wordcloud_hu_7b65bcc13669e74c.png)