Convex Hull Trick(CHT)

양성우·2024년 8월 5일

PS

목록 보기
1/1

CHT로 불리는 그냥 DP최적화 기법이다.

Convex Hull Trick 알고리즘은 직선의 집합에서 주어진
x 값에 대해 가장 낮거나 가장 높은 y 값을 효율적으로 찾는 데 사용됨.

이 알고리즘의 핵심 아이디어는 효율적으로 쿼리를 처리하기 위해 직선들을 "볼록 껍질"(convex hull) 특히 위로볼록 구조에 유지하는 것

구현

#include <iostream>
#include <deque>
using namespace std;

struct Line {
    long long a, b;
    double start; // 교점의 x 좌표

    Line(long long a = 0, long long b = 0, double start = -1e18) : a(a), b(b), start(start) {}

    long long value(long long x) const {
        return a*x + b;
    }

    double intersect(const Line& other) const {
        return (double)(other.b - b) / (a - other.a);
    }
};

class ConvexHullTrick {
private:
    deque<Line> hull;

public:
    void add_line(long long a, long long b) {
        Line newLine(a, b);
        while (hull.size() >= 2 && newLine.intersect(hull[0]) >= hull[0].intersect(hull[1])) {
            hull.pop_front();
        }
        hull.push_front(newLine);
    }

    long long query(long long x) {
        while (hull.size() >= 2 && hull.back().value(x) >= hull[hull.size() - 2].value(x)) {
            hull.pop_back();
        }
        return hull.back().value(x);
    }
};

대표 문제

백준 13263 나무자르기

dp[i] = i번째 나무까지의 최소 시간.
점화식 : dp[i] = min(dp[j] + b[j] * a[i]) (0<=j<i)중 최소값. 여기서 점화식 형태가 선형적이면 CHT적용 가능.

#include <iostream>
#include <vector>
#include <deque>
using namespace std;

struct Line {
    long long a, b;
    double start;

    Line(long long a = 0, long long b = 0, double start = -1e18) : a(a), b(b), start(start) {}

    long long value(long long x) const {
        return a * x + b;
    }

    double intersect(const Line& other) const {
        return (double)(other.b - b) / (a - other.a);
    }
};

class ConvexHullTrick {
private:
    deque<Line> hull;

public:
    void add_line(long long a, long long b) {
        Line newLine(a, b);
        while (hull.size() >= 2 && newLine.intersect(hull[0]) >= hull[0].intersect(hull[1])) {
            hull.pop_front();
        }
        hull.push_front(newLine);
    }

    long long query(long long x) {
        while (hull.size() >= 2 && hull.back().value(x) >= hull[hull.size() - 2].value(x)) {
            hull.pop_back();
        }
        return hull.back().value(x);
    }
};

int main() {
    int N;
    scanf("%d", &N);

    vector<long long> a(N + 1), b(N + 1), dp(N + 1);
    for (int i = 1; i <= N; ++i) {
        scanf("%lld", &a[i]);
    }
    for (int i = 1; i <= N; ++i) {
        scanf("%lld", &b[i]);
    }

    ConvexHullTrick cht;
    cht.add_line(b[1], 0); // 처음에는 첫번째 나무를 심는 것만 고려

    for (int i = 2; i <= N; ++i) {
        dp[i] = cht.query(a[i]);
        cht.add_line(b[i], dp[i]);
    }

    printf("%lld\n", dp[N]);
    return 0;
}

웬만하면 cout cin 대신 scanf, printf사용 하는게 낫다.

profile
mem 회로설계 / AI개발 / PS / OS / IC 설계 / AI 반도체 / FPGA / 디지털집적회로설계 / Verilog HDL

0개의 댓글