문제
- 문제 링크
BOJ 15249 - Building Bridges
$1$에서 $n$을 잇는 다리를 지어보려 한다.
문제의 비용 발생 정의를 따를 때, 최소로 연결하는 비용을 구해보자.
TL : $3$ sec, ML : $128$ MB
$2 ≤ n ≤ 10^5$ $0 ≤ h_i ≤ 10^6$ $0 ≤ |w_i| ≤ 10^6$
알고리즘 분류
- 다이나믹 프로그래밍(dp)
- 볼록 껍질을 이용한 최적화(convex hull trick)
풀이
결국 임의의 위치 $i$까지 다리를 지으려면 $1$에서 바로 짓냐, $1$에서 중간 지점 $j$를 거치고 $j$에서 오냐 로 구분해 볼 수 있다. (단, $j$까지의 최소 비용이 계산 됐다는 가정 하에.)
$j$ ~ $i$에 존재하는 기둥들의 $Σw$ 만큼 비용이 발생 하는데 이를 빠르게 계산할 수 있도록 누적 합( $p[]$ )을 이용하자.
이를 이용해 간단히 점화식을 정리해보면 $dp[i]$ : $1$ ~ $i$까지 다리를 짓는 데에 드는 최소 비용 이라 할 때
$dp[i] = min(dp[i], dp[j] + p[i - 1] - p[j] + (h[i] - h[j])^2)$ { $1 ≤ j < i$ }
로 정리할 수 있다.
이를 모든 $i$에 대한 $j$를 돌아보면 $O(N^2)$.
위 식을 우선 정리해보자.
$dp[j] + p[i - 1] - p[j] + (h[i] - h[j])^2$
$= dp[j] + p[i - 1] - p[j] + h[i]^2 - 2 * h[i] * h[j] + h[j]^2$
$j$의 변화에 관찰 되므로 $j$가 포함된 항들을 앞으로 빼보면
$= -2 * h[i] * h[j] + h[j]^2 + dp[j] - p[j] + h[i]^2 + p[i - 1]$
$h[i]$를 $x$로 보고 $Δj$의 변화와 관련 없는 항들을 상수로 빼면 $x$에 대한 일차 함수
$f(x) = -2 * h[j] * x + h[j]^2 + dp[j] - p[j]$ 로 정리할 수 있다.
단조성이 딱히 없어 보인다. 리차오 트리를 이용해 $dp$ 값들을 빠르게 계산하자.
전체 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #include<bits/stdc++.h> #define ll long long #define N 100001 using namespace std; struct L { ll p, q; inline ll f(ll x) { return p * x + q; } }; struct Node { ll l, r, s, e; L ln; }; vector <Node> T; ll n, i(1), M(1e18), a[N], b[N], dp[N]; void push(L q, ll n) { ll s(T[n].s), e(T[n].e), m((s + e) >> 1); L p = T[n].ln; if (p.f(s) > q.f(s)) swap(p, q); if (p.f(e) <= q.f(e)) { T[n].ln = p; return; } if (p.f(m) < q.f(m)) { T[n].ln = p; if (!~T[n].r) T[n].r = T.size(), T.push_back({ -1, -1, m + 1, e, { 0, M } }); push(q, T[n].r); return; } T[n].ln = q; if (!~T[n].l) T[n].l = T.size(), T.push_back({ -1, -1, s, m, { 0, M } }); push(p, T[n].l); } ll Q(ll x, ll n) { return !~n ? M : min(T[n].ln.f(x), Q(x, ((x << 1) <= T[n].s + T[n].e ? T[n].l : T[n].r))); } int main() { ios_base::sync_with_stdio(0); cin.tie(0); T.push_back({ -1, -1, 0, (ll)1e9, {0, M} }); for (cin >> n; i <= n; cin >> a[i++]); for (i = 1; i <= n; i++) cin >> b[i], b[i] += b[i - 1]; for (i = 2; i <= n; i++) push({ -2 * a[i - 1], a[i - 1] * a[i - 1] - b[i - 1] + dp[i - 1] }, 0), dp[i] = Q(a[i], 0) + a[i] * a[i] + b[i - 1]; cout << dp[n]; } | cs |
comment
끝
'Problem Solving > Baekjoon Online Judge (Diamond)' 카테고리의 다른 글
백준 17526 - Star Trek (C++) (0) | 2023.04.27 |
---|---|
백준 6171 - 땅따먹기 (C++) (0) | 2023.04.27 |
백준 14180 - 배열의 특징 (C++) (0) | 2023.04.26 |
백준 12795 - 반평면 땅따먹기 (C++) (0) | 2023.04.26 |
백준 4008 - 특공대 (C++) (0) | 2023.04.23 |