백준 4008 특공대 JAVA 풀이 (DP, CHT, 누적합, 시간복잡도)

2026. 4. 24. 22:06·코테(Solved.ac + Programmers)
728x90

🔗 문제 링크 

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


📌 문제 개요

병사들을 연속된 구간으로 나누고,
각 구간의 합을 x라고 할 때 ax² + bx + c의 점수를 계산하여
전체 점수의 최댓값을 구하는 문제

 

단순하게 모든 구간을 고려하면 시간복잡도가 O(N²)이 되어
N = 1,000,000에서 절대 풀 수 없다.


📌 접근 방법

이 문제의 핵심은 DP + 식 변형 + CHT 최적화입니다.

먼저 누적합을 정의합니다.

  • S[i] = x1 + x2 + ... + xi

그러면 구간 합은 다음처럼 계산됩니다.

  • 구간 합 = S[i] - S[j]

이제 DP를 정의합니다.

  • dp[i] = 1 ~ i까지 최대 점수
점화식: dp[i] = max(dp[j] + f(S[i] - S[j]))
 

여기서 그대로 계산하면 O(N²)이므로,
식을 전개해서 최적화해야 합니다.


📌 핵심 아이디어

식을 전개하면 다음과 같습니다.

dp[i]
= aS[i]^2 + bS[i] + c
+ max( (-2aS[j]) * S[i] + (dp[j] + aS[j]^2 - bS[j]) )
 

여기서 중요한 포인트:

  • j마다 하나의 직선을 만들 수 있음
  • 현재 x = S[i]에서 최대값을 찾는 문제로 변환됨

직선 정의

  • 기울기 m = -2aS[j]
  • 절편 k = dp[j] + aS[j]^2 - bS[j]

"직선 중에서 x에서 최대값 찾기 문제" → CHT 적용


📌 처리 흐름

입력
→ 누적합 S 계산
→ CHT 초기 직선 추가 (j=0)
→ i = 1 ~ N 반복
→ query(S[i])로 최적값 찾기
→ dp[i] 계산
→ 새로운 직선 추가
→ dp[N] 출력

📌 전체 코드

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

import java.io.*;
import java.util.*;

public class Main {

    static class Line {
        long m, b;
        Line(long m, long b) {
            this.m = m;
            this.b = b;
        }
    }

    static class CHT {
        ArrayList<Line> lines = new ArrayList<>();
        int ptr = 0;

        // l2가 필요 없는지 판단 (double 없이 처리)
        boolean isBad(Line l1, Line l2, Line l3) {
            return (l3.b - l1.b) * (l1.m - l2.m)
                    <= (l2.b - l1.b) * (l1.m - l3.m);
        }

        void add(long m, long b) {
            Line newLine = new Line(m, b);

            while (lines.size() >= 2 &&
                    isBad(lines.get(lines.size() - 2),
                            lines.get(lines.size() - 1),
                            newLine)) {
                lines.remove(lines.size() - 1);
            }

            lines.add(newLine);

            if (ptr >= lines.size()) ptr = lines.size() - 1;
        }

        long query(long x) {
            if (ptr >= lines.size()) ptr = lines.size() - 1;

            while (ptr + 1 < lines.size() &&
                    lines.get(ptr + 1).m * x + lines.get(ptr + 1).b
                            >= lines.get(ptr).m * x + lines.get(ptr).b) {
                ptr++;
            }

            return lines.get(ptr).m * x + lines.get(ptr).b;
        }
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());

        long a = Long.parseLong(st.nextToken());
        long b = Long.parseLong(st.nextToken());
        long c = Long.parseLong(st.nextToken());

        long[] S = new long[N + 1];
        long[] dp = new long[N + 1];

        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            S[i] = S[i - 1] + Long.parseLong(st.nextToken());
        }

        CHT cht = new CHT();

        // 초기 직선 (j = 0)
        cht.add(0, 0);

        for (int i = 1; i <= N; i++) {
            long x = S[i];

            long best = cht.query(x);

            dp[i] = a * x * x + b * x + c + best;

            long m = -2 * a * x;
            long k = dp[i] + a * x * x - b * x;

            cht.add(m, k);
        }

        System.out.println(dp[N]);
    }
}

📄 정리

백준 4008번은 단순 DP처럼 보이지만,
실제로는 누적합 + 식 전개 + CHT 최적화가 핵심인 문제

  • 구간 합은 누적합 차이로 바꾼다
  • dp[i] = max(dp[j] + 구간점수) 형태를 세운다
  • 식을 전개해서 직선의 최대값 문제로 변형한다
  • CHT를 이용해 빠르게 최적값을 찾는다

 

728x90

'코테(Solved.ac + Programmers)' 카테고리의 다른 글

백준 9654번 : 나부 함대 데이터 (JAVA)  (0) 2026.04.26
백준 [2851] 슈퍼 마리오 JAVA 풀이 (브루트포스, 누적합)  (0) 2026.04.25
백준 11444 피보나치 수 6 JAVA 풀이 (행렬 거듭제곱, 분할정복, 시간복잡도)  (0) 2026.04.24
백준 1149 RGB 거리 JAVA 풀이 (DP, 누적 최소값, 시간복잡도)  (0) 2026.04.24
[백준] 트리 순회 JAVA 풀이 (트리, 재귀, DFS)  (0) 2026.04.21
'코테(Solved.ac + Programmers)' 카테고리의 다른 글
  • 백준 9654번 : 나부 함대 데이터 (JAVA)
  • 백준 [2851] 슈퍼 마리오 JAVA 풀이 (브루트포스, 누적합)
  • 백준 11444 피보나치 수 6 JAVA 풀이 (행렬 거듭제곱, 분할정복, 시간복잡도)
  • 백준 1149 RGB 거리 JAVA 풀이 (DP, 누적 최소값, 시간복잡도)
PUSH → MERGE → DEPLOY
PUSH → MERGE → DEPLOY
데이터 흐름과 운영 자동화를 설계하는 백엔드 개발자
  • PUSH → MERGE → DEPLOY
    Coding Dongin
    PUSH → MERGE → DEPLOY
  • 전체
    오늘
    어제
    • MEUN
      • 코테(Solved.ac + Programmers)
      • BootCamp(JAVA)
      • JAVA
      • SpringBoot
      • JavaScript
      • JSP
      • DB(SQL)
      • React
      • HTML_CSS
      • jQuery
      • SCSS
      • GSAP
      • 설치 + 꿀팁
      • 정보처리기사 오답노트
      • 정보처리기사 기출문제
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GIT
  • 공지사항

  • 인기 글

  • 태그

    Level2
    정보처리기사
    level0
    시뮬레이션
    프로그래머스
    solved.ac
    springboot
    정처기실기
    정보처리기사 실기 기출문제
    정렬
    실기
    백엔드개발자
    dp
    수학
    완전탐색
    백준
    피보나치
    level1
    자바
    배열
    문자열
    정처기오답노트
    알고리즘
    기출문제
    구현
    브루트포스
    코딩테스트
    자료구조
    java
    정처기
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.4
PUSH → MERGE → DEPLOY
백준 4008 특공대 JAVA 풀이 (DP, CHT, 누적합, 시간복잡도)
상단으로

티스토리툴바