본문 바로가기

알고리즘 관련/BOJ

BOJ)10167 금광

문제: icpc.me/10167


직사각형 모양의 영역으로 금광을 개발 할 때 얻을 수 있는 최대 이익을 구하는 문제이다.


문제를 푸는데 상당히 고생했는데 KOI 2014 중등부 문제인걸 보고 어이가없어서 웃음만 나왔다... 


일단 N이 3000이므로 N^2혹은 N^2logN 알고리즘을 사용해야 하는데 


처음 생각한것은 x축기준으로 스위핑 하여 y축을 세그먼트 트리 구간합에 업데이트 시키는 작업을 순서대로 하는데

이 때 이 작업은 전단계의 점들을 반드시 포함하게 되므로 모든점에 대해서 이 작업을 반복하는 것이다.


이 때 처음에는 최대 구간합을 구하기 위해서 이미 업데이트 된 모든 점이랑 비교하여 구간합의 최댓값을 구했는데


이렇게 할 경우 한점에서 업데이트한 후 구간합의 최댓값을 구하는데 O(NlogN) 여기에 전부 업데이트 할 시 O(N^2logN) 

하지만 우리는 모든 점에 대해서 x좌표가 커지는 쪽으로 업데이트 해야하므로 O(N^3logN)이 사용된다.


n개의 점에 대해서 x좌표가 커지는 쪽으로 n만큼 업데이트 시켜야 하는건 복잡도를 줄일 수 없으므로 한점을 갱신 한 뒤 

구간의 최댓값을 구하는걸 O(NlogN)에서 O(logN)으로 줄여야 한다.


이걸 고민하다가 kks227님의 블로그를 보고 알게 되었는데 


분할정복으로 구할 수 있었다.


한 구간에서 

LSUM ->그 구간의 왼쪽 끝에서부터의 연속한 원소의 최대 합

RSUM ->그 구간의 오른쪽 끝에서부터의 연속한 원소의 최대 합

TOTALSUM ->그 구간의 모든 원소의 합

MAXSUM-> 그 구간에서 나올 수 있는 최대 연속합 

이라 정의할 때 


LSUM=max(왼쪽자식의LSUM, 왼쪽자식의TOTALSUM+오른쪽 자식의 LSUM)

RSUM=max(오른쪽자식의RSUM, 오른쪽자식의TOTALSUM+왼쪽 자식의RSUM)

TOTALSUM=왼쪽자식의TOTALSUM+오른쪽자식의TOTALSUM

MAXSUM=max(왼쪽자식의RSUM+오른쪽자식의LSUM, 왼쪽자식의MAXSUM, 오른쪽자식의 MAXSUM, LSUM, RSUM)


이란 수식을 얻을수 있어


우리는 갱신과 MAXSUM계산을 세그먼트 트리를 이용하여 O(logN)에 처리할 수 있다.


참고로 업데이트 하는 y좌표는 좌표압축을 이용하여 처리해주면 된다. 그리고 답은 long long 범위이다.


+추가)주의 할 점은 좌표를 볼 때 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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define MAX_N 3000
using namespace std;
typedef long long ll;
ll n, x, y, z, res;
vector<ll> ypos;
ll max(ll a, ll b, ll c, ll d,ll e) {
    return max(max(a, b), max(c, max(d, e)));
}
struct element {
    ll x, y, z;
    bool operator<(const element &A)const {
        return x < A.x;
    }
};
struct mseg {    //왼쪽 끝부터 최대 연속 합,오른쪽 끝부터 최대 연속 합,구간 합, 구간의 최대 연속 합
    ll lsum, rsum, tsum, msum;
};
mseg seg[* MAX_N];
element ele[MAX_N];
ll get_ypos(ll pos) {
    return lower_bound(ypos.begin(), ypos.end(), pos) - ypos.begin();
}
void update(ll pos, ll val, ll node, ll x, ll y) {
    if (y < pos || pos < x)
        return;
    if (x == y) {
        seg[node].tsum += val;
        seg[node].lsum += val;
        seg[node].rsum += val;
        seg[node].msum += val;
        return;
    }
    ll mid = (x + y) >> 1;
    update(pos, val, node * 2, x, mid);
    update(pos, val, node * + 1, mid + 1, y);
    seg[node].lsum = max(seg[node * 2].lsum, seg[node * 2].tsum + seg[node * + 1].lsum);
    seg[node].rsum = max(seg[node * + 1].rsum, seg[node * + 1].tsum + seg[node * 2].rsum);
    seg[node].tsum = seg[node * 2].tsum + seg[node * + 1].tsum;
    seg[node].msum = max(seg[node * 2].rsum + seg[node * + 1].lsum, seg[node * 2].msum, seg[node * + 1].msum, seg[node].lsum, seg[node].rsum);
}
ll query(ll lo, ll hi, ll node, ll x, ll y) {
    if (y < lo || hi < x)
        return 0;
    if (lo <= x&&<= hi)
        return seg[node].msum;
    ll mid = (x + y) >> 1;
    return max(query(lo, hi, node * 2, x, mid), query(lo, hi, node * + 1, mid + 1, y));
}
int main() {
    scanf("%lld"&n);
    for (int i = 0; i < n; i++) {
        scanf("%lld%lld%lld"&x, &y, &z);
        ele[i] = { x,y,z };
        ypos.push_back(y);
    }
    sort(ele, ele + n);
    sort(ypos.begin(), ypos.end());
    ypos.erase(unique(ypos.begin(), ypos.end()), ypos.end());
    const ll MAX_Y = ypos.size();
    for (int i = 0; i < n; i++) {
        memset(seg, 0sizeof(seg));
        if (i&&ele[i].x == ele[i - 1].x)
            continue;
        for (int j = i; j < n; j++) {
            ll pos = get_ypos(ele[j].y);
            update(pos, ele[j].z, 10, MAX_Y - 1);
            if (j != n - && ele[j].x == ele[j + 1].x)
                continue;
            ll q = query(0, MAX_Y - 110, MAX_Y - 1);
            res = max(q, res);
        }
    }
    printf("%lld\n", res);
    return 0;
}
cs


'알고리즘 관련 > BOJ' 카테고리의 다른 글

BOJ)2775 부녀회장이 될테야  (0) 2017.01.15
BOJ)5480 전함  (0) 2017.01.14
BOJ)9184 신나는 함수 실행  (0) 2017.01.14
BOJ)1049 기타줄  (0) 2017.01.14
BOJ)2022 사다리  (2) 2017.01.14