Post

[백준] 2357번 - 최솟값과 최댓값 [Java][C++]

[백준] 2357번 - 최솟값과 최댓값 [Java][C++]

문제 링크


1. 문제 풀이

수열의 길이 $N$ 이 최대 $100,000$, 주어진 구간의 최솟값과 최댓값을 구하는 쿼리의 수 $M$ 이 최대 $100,000$ 인 문제로 쿼리 당 $O(\log{N})$ 의 시간복잡도로 해결할 수 있는 세그먼트 트리를 활용하면 해결할 수 있다. 구간의 최솟값과 최댓값을 구하며 변경 쿼리가 없다는 점에서 희소 테이블을 활용해도 해결할 수 있다.


2. 코드

1. 세그먼트 트리 [Java]

최솟값과 최댓값을 필드로 갖는 Node 클래스를 만들고 Node 객체에 대한 세그먼트 트리를 구현하는 방식으로 했다. merge 메서드로 상위 노드에서 구간의 대푯값을 갱신하게 해줬다. 항등원은 최솟값에는 가능한 최댓값, 최댓값에는 가능한 최솟값을 넣은 Node 객체가 된다.

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
import java.io.*;
import java.util.*;

public class Main {

    static final int MIN = 0;
    static final int MAX = 1_000_000_001;

    static class Node {
        int min;
        int max;

        public Node(int min, int max) {
            this.min = min;
            this.max = max;
        }
    }

    static class SegmentTree {
        int n;
        Node[] tree;

        public SegmentTree(int[] arr) {
            this.n = arr.length - 1;
            this.tree = new Node[4 * n];
            init(arr, 1, 1, n);
        }

        void init(int[] arr, int node, int start, int end) {
            if (start == end) {
                tree[node] = new Node(arr[start], arr[start]);
                return;
            }

            int mid = (start + end) / 2;

            init(arr, node * 2, start, mid);
            init(arr, node * 2 + 1, mid + 1, end);
            tree[node] = merge(tree[node * 2], tree[node * 2 + 1]);
        }

        Node merge(Node a, Node b) {
            return new Node(Math.min(a.min, b.min), Math.max(a.max, b.max));
        }

        String query(int left, int right) {
            Node res = query(1, 1, n, left, right);
            return res.min + " " + res.max;
        }

        Node query(int node, int start, int end, int left, int right) {
            if (left > end || right < start) return new Node(MAX, MIN);
            if (left <= start && end <= right) return tree[node];

            int mid = (start + end) / 2;

            Node leftRes = query(node * 2, start, mid, left, right);
            Node rightRes = query(node * 2 + 1, mid + 1, end, left, right);
            return merge(leftRes, rightRes);
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        int[] arr = new int[1 + N];
        for (int i = 1; i <= N; i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }

        SegmentTree tree = new SegmentTree(arr);
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            sb.append(tree.query(a, b)).append("\n");
        }

        System.out.println(sb);
    }
}

2. 희소 테이블 [Java]

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
import java.io.*;
import java.util.*;

public class Main {

    static final int LOG = 20;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        int[] arr = new int[N];
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }

        int[] lg = new int[1 + N];
        for (int i = 2; i <= N; i++) {
            lg[i] = lg[i / 2] + 1;
        }

        int[][] stMin = new int[LOG][N];
        int[][] stMax = new int[LOG][N];
        System.arraycopy(arr, 0, stMin[0], 0, N);
        System.arraycopy(arr, 0, stMax[0], 0, N);

        for (int k = 1; k < LOG; k++) {
            for (int i = 0; i + (1 << k) <= N; i++) {
                stMin[k][i] = Math.min(stMin[k - 1][i], stMin[k - 1][i + (1 << (k - 1))]);
                stMax[k][i] = Math.max(stMax[k - 1][i], stMax[k - 1][i + (1 << (k - 1))]);
            }
        }

        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken()) - 1;
            int b = Integer.parseInt(st.nextToken()) - 1;

            int k = lg[b - a + 1];
            sb.append(Math.min(stMin[k][a], stMin[k][b - (1 << k) + 1])).append(" ")
                    .append(Math.max(stMax[k][a], stMax[k][b - (1 << k) + 1])).append("\n");
        }

        System.out.println(sb);
    }
}

3. 세그먼트 트리 [C++]

최솟값과 최댓값을 pair 로 관리하는 세그먼트 트리를 구현하는 방식으로 했다. merge 메서드로 상위 노드에서 구간의 대푯값을 갱신하게 해줬다. 항등원은 최솟값에는 가능한 최댓값, 최댓값에는 가능한 최솟값을 넣은 pair 객체가 된다.

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;

constexpr int MIN = 0;
constexpr int MAX = 1000000001;

struct SegTree {
    int n;
    vector<pair<int, int>> tree;

    SegTree(int n) : n(n), tree(4 * n) {
    }

    void init(const vector<int>& arr, int node, int start, int end) {
        if (start == end) {
            tree[node] = {arr[start], arr[start]};
            return;
        }

        int mid = (start + end) / 2;

        init(arr, node * 2, start, mid);
        init(arr, node * 2 + 1, mid + 1, end);
        tree[node] = merge(tree[node * 2], tree[node * 2 + 1]);
    }

    pair<int, int> merge(pair<int, int> a, pair<int, int> b) {
        return {min(a.first, b.first), max(a.second, b.second)};
    }

    pair<int, int> query(int node, int start, int end, int left, int right) {
        if (left > end || right < start) return {MAX, MIN};
        if (left <= start && end <= right) return tree[node];

        int mid = (start + end) / 2;

        pair<int, int> leftRes = query(node * 2, start, mid, left, right);
        pair<int, int> rightRes = query(node * 2 + 1, mid + 1, end, left, right);
        return merge(leftRes, rightRes);
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector<int> v(n);
    for (int& x : v) cin >> x;

    SegTree tree(n);
    tree.init(v, 1, 0, n - 1);

    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;

        pair<int, int> res = tree.query(1, 0, n - 1, a - 1, b - 1);
        cout << res.first << ' ' << res.second << '\n';
    }
}

4. 희소 테이블 [C++]

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
#include <bits/stdc++.h>
using namespace std;

constexpr int LOG = 20;
constexpr int MAXN = 100000;

int a[MAXN];
int stMin[LOG][MAXN];
int stMax[LOG][MAXN];
int lg[1 + MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> a[i];

    for (int i = 2; i <= n; i++) {
        lg[i] = lg[i / 2] + 1;
    }

    for (int i = 0; i < n; i++) {
        stMin[0][i] = a[i];
        stMax[0][i] = a[i];
    }

    for (int k = 1; k < LOG; k++) {
        for (int i = 0; i + (1 << k) <= n; i++) {
            stMin[k][i] = min(stMin[k - 1][i], stMin[k - 1][i + (1 << (k - 1))]);
            stMax[k][i] = max(stMax[k - 1][i], stMax[k - 1][i + (1 << (k - 1))]);
        }
    }

    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        a--;
        b--;

        int k = lg[b - a + 1];
        int mn = min(stMin[k][a], stMin[k][b - (1 << k) + 1]);
        int mx = max(stMax[k][a], stMax[k][b - (1 << k) + 1]);
        cout << mn << ' ' << mx << '\n';
    }
}

3. 풀이 정보

1. 세그먼트 트리 [Java]

언어시간메모리코드 길이
Java 11760 ms172076 KB2537 B

2. 희소 테이블 [Java]

언어시간메모리코드 길이
Java 11668 ms83976 KB1664 B

3. 세그먼트 트리 [C++]

언어시간메모리코드 길이
C++ 1780 ms5544 KB1590 B

4. 희소 테이블 [C++]

언어시간메모리코드 길이
C++ 1764 ms18428 KB1070 B

This post is licensed under CC BY 4.0 by the author.