Post

Convex Hull Trick dalam Dynamic Programming

(Post in Indonesian) Trik khusus dalam Dynamic Programming.

Convex Hull Trick dalam Dynamic Programming

DISCLAIMER: Post ini memuat slide saja. Slide ini dibuat waktu saya diminta bantuan oleh Pak Denny untuk mengisi kelas Competitive Programming waktu saya masih kuliah. Sekarang, saya sudah tidak lagi aktif dalam dunia competitive programming. Mohon maaf apabila ada kesalahan 🙏🙏🙏, source code untuk latexnya pun sudah hilang.

Materi

Slide bisa diunduh disini.

Contoh soal

SPOJ GOODG

Untuk soal, klik disini

Solusinya (dalam C++) bisa dilihat di bawah:

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <bits/stdc++.h>
#define INF 1000000000
using namespace std;
typedef long long LL;

// Struktur data garis dalam bentuk ax + by = c
struct Line{ //ax+by=c
	LL a,b,c;
	
  // Komponen x pada titik potong
  double intersect(Line l) {
		assert(a*l.b != b*l.a);
		return (double)((c*l.b) - (b*l.c))/(double)((a*l.b)-(b*l.a));
	}

	LL evaluate(LL x) {
		return c-(a*x);
	}
};

// Struktur data untuk menyimpan garis-garis relevan
struct RelevantLines{
    // Vector sebagai stack
    vector<Line> lines;

    void insert_line(Line l) {
        Line l3 = l;
        while (lines.size() > 1) {
            int sz = (int)lines.size();
            Line l1 = lines[sz-2];
            Line l2 = lines[sz-1];
            double x_12 = l1.intersect(l2);
            double x_13 = l1.intersect(l3);
            
            if (x_13 <= x_12) lines.pop_back();
            else break;
        }
        
        lines.push_back(l3);
    }

    pair<double, double> get_interval(int line_idx) {
        double left_interval = -INF;
        double right_interval = INF;

        if (line_idx > 0)
            left_interval = lines[line_idx].intersect(lines[line_idx-1]);

        if (line_idx < (int)lines.size()-1)
            right_interval = lines[line_idx].intersect(lines[line_idx+1]);

        return make_pair(left_interval, right_interval);
    }

    // Binary search
    LL query(LL x) {
        int l = 0;
        int r = lines.size()-1;
        int mid;
        while (l<=r) {
            mid = (l+r)/2;
            pair<double, double> interval = get_interval(mid);
            if ((interval.first <= x) && (x <= interval.second))
                return lines[mid].evaluate(x);
            else if (x > interval.second)
                l = mid + 1;
            else
                r = mid - 1;
        }

        // Shouldn't have reached here
        assert(false);
    }
};

// y = mx + c menjadi (-m)x + y = c (ax + by = c)
Line make_line(LL m, LL c) {
    return (Line){-m, 1, c};
}


LL a[1000005], d[1000005];
LL V[1000005];
int main() {
    int N;
    cin.sync_with_stdio(0);
    cin.tie(0);

    cin >> N;
    for (int i=1;i<=N;++i)
        cin >> a[i] >> d[i];
    
    LL ans = 0;
    RelevantLines y_rel;
    memset(V, 0, sizeof V);
    
    y_rel.insert_line(make_line(-(N+1), 0));
    for (LL i=N;i>=1;--i) {
        LL maxval = y_rel.query(d[i]);
        V[i] = a[i] + maxval + i*d[i];
        ans = max(ans, V[i]);
        y_rel.insert_line(make_line(-i, V[i]));
    }

    cout << ans << '\n';
    return 0;
}
This post is licensed under CC BY 4.0 by the author.