코딩/백준 문제 풀이

BOJ 13263 - 나무 자르기

stonejjun 2020. 3. 28. 23:55

문제 태그 : https://www.acmicpc.net/problem/13263

문제 소개

나무를 모두 자르고 싶다. 나무 N개의 길이$a_{i}$가 주어지고 N개의 $b_{i}$가 주어진다. 나무 길이 1을 자를때마다 다시 충전을 해야한다. 이때 충전하는 비용은 지금까지 완벽히 자른 나무의 번호 중 가장 큰 번호의 $b_{i}$이다.

이때 $a_{1}=1$ 이고, $b_{N}=0$ 이며, $a_{i}$는 증가하고 $b_{i}$는 감소한다.

문제 풀이

제일 중요한 사실은 $b_{N}=0$이라는 것이다. N번 나무를 자르면 그 이후로는 드는 비용이 0이기 때문에 이 문제를 다르게 해석하면 N번 나무를 자르는데 얼마나 비용이 드는가? 이다.

한 번에 N번 나무를 자를지 중간에 비용을 감소 시킨후 자르는 것이 좋은지 잘 판단을 해야한다. 당연히 N번 나무 이후에 다 공짜 이므로 그 전에는 $j<i$ 인 j에 대해서 i 이후에 j를 자를 이유는 없다.

$DP[i]$를 i까지 자르는데 드는 최소 비용으로 정의하자.
그러면 $DP[i]=\underset{j<i}{min}(b_{j}*a_{i}+DP[j])$ 라는 식이 나온다. 
N의 제한은 100000이므로 $O(N^2)$의 시간복잡도로는 해결할 수 없고 따라서 CHT를 이용해야 한다. 
(CHT를 모른다면 이 글을 보고 오자.)

$ax+b$의 직선으로 만들어진 볼록껍질에서 x에 $a_{i}$를 넣어 $DP[i]$를 구하고 다시 $a=b_{i}$, $b=DP[i]$인 직선을 넣어가면서 볼록껍질을 조금씩 업데이트 해주면 된다.

※ 마지막은 충전을 안해도 됨으로 자르기 직전에 충전을 한다고 생각하자.
그러면 첫 나무의 길이는 1이므로 DP[1]은 항상 0이 될 것이고, $y=b_{1}x$ 의 직선이 항상 맨 처음에 들어갈 것이다.

코드

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
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<ll,ll> pii;
#define ff first
#define ss second
 
struct lin{ 
    ll a,b;
};
 
ll sz=0;
lin stk[1010101];
double cx(ll a,ll b) {
    return 1.0 * (double)(stk[a].b - stk[b].b) / (stk[b].a - stk[a].a);
}
void insert(lin v){
    stk[sz] = v;
    while(1<sz&&cx(sz-2,sz-1)>cx(sz-1,sz)){
        stk[sz-1= stk[sz];
        sz--;
    }
    sz++;
}
ll sol(ll x){ 
    ll lo=0,hi=sz-1;
    while(lo<hi){
        ll mid=(lo+hi)/2;
        if (cx(mid,mid+1)<=x) lo=mid+1;
        else hi=mid;
    }
    return stk[lo].a*x+stk[lo].b;
}
void clear(){
    sz=0;
}
 
ll arr[1010101];
ll brr[1010101];
ll dp[1010101];
 
int main(){
    ll i,j,k,m,n;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
        scanf("%lld",&arr[i]);
    for(i=1;i<=n;i++)
        scanf("%lld",&brr[i]);
    
    lin l;
    l.a = brr[1];
    l.b = 0;
    insert(l);
    for(i=2;i<=n;i++){
        dp[i]=sol(arr[i]);
        l.a = brr[i];
        l.b = dp[i];
        insert(l);
    }
    printf("%lld",dp[n]);
    return 0;
}
 
Colored by Color Scripter

'코딩 > 백준 문제 풀이' 카테고리의 다른 글

BOJ 15134 - Dividing Marbles  (0) 2020.04.03
BOJ 4008 - 특공대  (1) 2020.03.30
BOJ 17940 - 지하철  (0) 2020.03.26
BOJ 13925 - 수열과 쿼리 13  (0) 2020.03.17
BOJ 7791 - KBTU Party  (1) 2020.03.13