본문 바로가기

Problem Solving/문제풀이

백준 6171번 땅따먹기

반응형

문제 요약

N개의 직사각형이 주어진다. 각 직사각형의 넓이는 $W_i * H_i$로 주어집니다.

각 직사각형의 가격은 $W_i*H_i$가 됩니다. 이 때, 여러 개의 땅을 묶어서 살 때는 묶음의 가격이 $(maxW_i) * (maxH_i)$가 됩니다.

이 때, 모든 땅을 살 수 있는 최소의 비용을 구하는게 목적이다.

풀이

묶음으로 살 때 가격을 정하는 방식 때문에 $W_i \ge W_j$이고 $H_i \ge H_j$면 j번째 땅은 무시할 수 있습니다.

이러한 전처리는 $W$의 오름차순으로 땅을 정렬한 뒤에 순서대로 보면서 지워질 대상을 지워나가면 전부 지울 수 있습니다.

각 땅을 $H$의 내림차순이면서 $W$는 오름차순인 상태로 만들 수 있습니다.

이러한 전처리가 끝난 상태의 땅들에 대해서 다음과 같은 dp식을 생각합시다.
$$
\begin{aligned}
dp(i) &= \text{1번부터 i번 땅까지 샀을 때의 최소비용} \\
dp(i) &= \underset{j<i}{min}(h_{j+1}*W_i + dp(j))
\end{aligned}
$$
이는 Convex Hull Trick으로 처리 가능합니다.

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
const ll INF = 1e10;
int N;
pll a[50005], b[50005];
ll dp[50005];
int ptr;
struct Line {
    ll m, b;
    double x;
    Line(ll _m, ll _b, double _x) : m(_m), b(_b), x(_x) {};
    ll f(ll x) {
        return m * x + b;
    }
};
vector<Line> lines;

double intersect(Line& a, Line& b) {
    return (double)(b.b - a.b) / (a.m - b.m);
}

void addLine(ll m, ll b) {
    Line a(m, b, -INF);
    if (lines.empty()) {
        lines.push_back(a);
        return;
    }
    while (!lines.empty()) {
        Line top = lines.back();
        double x = intersect(top, a);
        if (x <= top.x) lines.pop_back();
        else break;
    }
    a.x = intersect(lines.back(), a);
    lines.push_back(a);
    if (ptr >= lines.size()) ptr = lines.size() - 1;
    return;
}

ll query(ll x) {
    while (ptr < lines.size() - 1 && lines[ptr + 1].x < x) ++ptr;
    return lines[ptr].f(x);
}

int main() {
    cin.tie(nullptr); ios::sync_with_stdio(false);
    cin >> N;
    for (int i = 0; i < N; ++i) cin >> a[i].first >> a[i].second;
    sort(a, a + N);
    int idx = 0;
    for (int i = 0; i < N; ++i) {
        while (idx > 0 && b[idx - 1].second <= a[i].second) --idx;
        b[idx++] = a[i];
    }
    N = idx;
    addLine(b[0].second, 0);
    for (int i = 0; i < N - 1; ++i) {
        dp[i] = query(b[i].first);
        addLine(b[i + 1].second, dp[i]);
    }
    cout << query(b[N - 1].first) << '\n';
    return 0;
}
반응형

'Problem Solving > 문제풀이' 카테고리의 다른 글

백준 5254번 Balls  (0) 2021.02.15
백준 17526번 Star Trek  (0) 2021.02.15
백준 13263번 나무 자르기  (0) 2021.02.15
백준 15249번 Building Bridges  (0) 2021.02.15
백준 4008번 특공대  (0) 2021.02.15