알고리즘 공부/문제 풀이

[JAVA] 백준 2357 최솟값과 최댓값

valid_ming 2021. 9. 29. 15:05

https://www.acmicpc.net/problem/2357

 

2357번: 최솟값과 최댓값

N(1 ≤ N ≤ 100,000)개의 정수들이 있을 때, a번째 정수부터 b번째 정수까지 중에서 제일 작은 정수, 또는 제일 큰 정수를 찾는 것은 어려운 일이 아니다. 하지만 이와 같은 a, b의 쌍이 M(1 ≤ M ≤ 100

www.acmicpc.net

문제 분석

N개의 정수 범위에서 a ~ b까지의 최댓값과 최솟값을 구해라. a, b쌍이 최대 10만 개 주어질 수 있음!

=> 각 a, b의 쌍에 대해 최솟값과 최댓값을 구하면 시간초과 발생

해결 전략

구간 합을 로그의 시간복잡도롤 구할 수 있는 세그먼트 트리 (이진 트리의 형태로 왼쪽, 오른쪽 자식을 재귀적으로 탐색)

최댓값과 최솟값을 구하기 위한 세그먼트 트리를 만들자

- mode에 따라 최솟값, 최댓값 메서드 구현

- 리프 노드를 제외한 노드에는 최댓값과 최솟값의 정보를 가짐

- 기존의 sum 연산 대신 find 연산을 수행!

** root 노드는 1번 노드부터 init, find 메서드 시작, arr: 1번 부터 값 가지게 하기

코드

import java.util.*;
import java.io.*;

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

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

        int[] maxTree = new int[4*N];
        int[] minTree = new int[4*N];
        init(arr, maxTree, 1, 1, N, 1);
        init(arr, minTree, 1, 1, N, 0);

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

            int min = find(minTree, 1, 1, N, a, b, 0);
            int max = find(maxTree, 1, 1, N, a, b, 1);
            sb.append(min+" "+max).append('\n');
        }

        System.out.println(sb);
    }

    public static int init(int[] arr, int[] tree, int node, int start, int end, int mode){
        if(start==end) return tree[node] = arr[start];

        int mid = (start+end)/2;
        int left = init(arr, tree, node*2, start, mid, mode);
        int right = init(arr, tree, node*2+1, mid+1, end, mode);

        if(mode==1) return tree[node] = Math.max(left, right);
        else return tree[node] = Math.min(left, right);
    }

    public static int find(int[] tree, int node, int start, int end, int left, int right, int mode){
        if(end < left || start > right){
            if(mode==0) return Integer.MAX_VALUE;
            else return 0;
        }
        if(left<=start && end<=right) return tree[node];

        int mid = (start+end)/2;
        int lc = find(tree, node*2, start, mid, left, right, mode);
        int rc = find(tree, node*2+1, mid+1, end, left, right, mode);

        if(mode==0) return Math.min(lc, rc);
        else return Math.max(lc, rc);
    }
}