코딩/백준 문제 풀이

BOJ 4008 - 특공대

stonejjun 2020. 3. 30. 23:50

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

문제 소개

특정 값 a,b,c와 N명의 전투원들의 전투력이 주어진다. 전투원들은 인접한 전투원들끼리 묶어 몇개의 그룹으로 나눌 것이다. 각 그룹의 조정된 전투력은 그 그룹에 속한 모든 전투원의 전투력의 합을 x라고 했을 때, $ax^{2}+bx+c$이다. 이때 가질 수 있는 그룹의 조정된 전투력의 합의 최댓값을 구하시오.

문제 풀이

이 문제를 딱 보자마다 떠오르는 생각은 DP이다. '인접한 것 끼리 묶는다'도 DP를 쓰기 굉장히 좋은 조건이며, 정확히 DP[i]=1부터 i 까지 로 문제를 해결했을 때의 값으로 정의하면 깔끔하게 해결될 것 처럼 보인다. 이를 통해 DP값에 대한 식을 세워보면 다음과 같이 나온다. 
$DP[i]=\underset{j<i}{min}(DP[j]+$(j+1부터 i까지 그룹의 값)$)$ ... 1번식

이 문제를 해결하려면 $O(N^{2})$의 시간복잡도가 필요하다. 따라서 DP[j]+(j+1부터 i까지 그룹의 값)에 좀 더 집중을 해서 관찰을 해야한다. 일단 (j+1부터 i까지 그룹의 값)을 구하기 위해서는 j+1부터 i까지의 부분 합이 필요하다. 하지만 전처리를 해도 구간이 $N^2$ 스케일의 갯수가 나오게 된다.
이 문제를 해결하면서 j+1과 j의 연관성을 줄 수 있는 방법은 prefix sum을 이용하는 것이다. 1부터 i까지의 prefix sum을 $pre[i]$라고 하면 j+1부터 i까지의 부분 합은 $pre[i]-pre[j]$가 될 것이다. 이를 이용하여 1번식을 다시쓰면,

$DP[i]=\underset{j<i}{min}(DP[j]+a(pre[i]-pre[j])^{2}+b(pre[i]-pre[j])+c)$라는 식이 나오게 된다.
이렇게 하면 구간합은 해결이 되지만, 임의의 i,j 쌍에 대해 모두 확인해야 하므로 여전히 시간복잡도는 $O(N^2)$을 가지게 된다. 하지만 식을 좀 더 정리해 보면 CHT를 쓸 수 있는 형태라는 것을 알 수 있다.

$(-2\cdot a\cdot pre[j]+ b)\cdot pre[i] +a\cdot pre[j]^2 +c+a\cdot pre[i]^2 $
위와 같은 식이 나오게 되고, pre[i]의 배수이면서 j와 관련된 부분, pre[i]와 관련이 없으면서 j와 관련된 부분, 그 밖의 부분으로 분리가 되고, 이는 $ax+b+c$ 꼴을 갖추게 된다. c는 추가적으로 i와 관련해서 더해주는 값이기 때문에 CHT를 이용하는데 지장이 없다. 

따라서 CHT를 이용하여 $O(NlgN)$에 이 문제를 해결 할 수 있다. 

코드

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
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
 
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 x[1010101];
ll pre[1010101];
ll dp[1010101];
 
int main(){
    ll a,b,c,n,i,j,k;
    scanf("%lld",&n);
    scanf("%lld %lld %lld",&a,&b,&c);
    for(i=1;i<=n;i++){
        scanf("%lld",&x[i]);
        pre[i] = pre[i-1]+x[i];
    }
    lin l;
    l.a = b;
    l.b = 0;
    insert(l);
    for(i=1;i<=n;i++){
        dp[i] = c+a*pre[i]*pre[i]+sol(pre[i]);
        l.a = -2*a*pre[i]+b;
        l.b = dp[i]-b*pre[i]+a*pre[i]*pre[i];
        insert(l);
    }
    printf("%lld",dp[n]);
    return 0;
}
 
cs

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

BOJ 13409 - Black and White Boxes  (0) 2020.04.04
BOJ 15134 - Dividing Marbles  (0) 2020.04.03
BOJ 13263 - 나무 자르기  (0) 2020.03.28
BOJ 17940 - 지하철  (0) 2020.03.26
BOJ 13925 - 수열과 쿼리 13  (0) 2020.03.17