Algorithm/세그먼트 트리

BOJ#14438 수열과 쿼리 17

밤이2209 2017. 6. 1. 16:57

BOJ#14438 수열과 쿼리 17


* 문제



* 풀이

Segment Tree - RMQ



* 나의 코드 (Java, Kotlin)


(Java)
import java.io.*;
import java.util.StringTokenizer;

/**
* 선데이코딩 베타라운드1 - E. 수열의 최소값
* BOJ#14439 수열과 쿼리 17
* https://www.acmicpc.net/problem/14438
*/

public class Main {

static final int QUERY_CHANGE = 1;
static final int QUERY_FIND_MIN = 2;

public static void main(String[] args) throws IOException {

int N; // 수열의 크기
int nQuery; // 쿼리의 개수
int[] A;

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

N = Integer.parseInt(br.readLine());

A = new int[N];

StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < N; i++) {

A[i] = Integer.parseInt(st.nextToken());
}

// solve

SegmentTree segmentTree = new SegmentTree(A, N);

nQuery = Integer.parseInt(br.readLine());

while (nQuery-- > 0) {

st = new StringTokenizer(br.readLine());

int queryType = Integer.parseInt(st.nextToken());


// QUERY_CHANGE
if (queryType == QUERY_CHANGE) {

int i = Integer.parseInt(st.nextToken()) - 1;
int j = Integer.parseInt(st.nextToken());

segmentTree.update(i, j, 1, 0, N - 1);
}
// QUERY_FIND_MIN
else if (queryType == QUERY_FIND_MIN) {

int i = Integer.parseInt(st.nextToken()) - 1;
int j = Integer.parseInt(st.nextToken()) - 1;

bw.write(segmentTree.query(i, j, 1, 0, N - 1) + "\n");
}

} // ~while loop

bw.flush();

bw.close();
br.close();
}
}

class SegmentTree {

final int MAX_VALUE = Integer.MAX_VALUE;
int[] segmentArr; // The array that stores segment tree nodes

SegmentTree(int[] arr, int n) {

int x = (int) Math.ceil(Math.log(n) / Math.log(2));

int segmentSize = (int) Math.pow(2, x) * 2;

segmentArr = new int[segmentSize];

init(arr, 0, n - 1, 1);
}

// node를 root로 하는 서브트리를 초기화하고, 이 구간의 최소치를 반환한다.
int init(int[] arr, int left, int right, int node) {

if (left == right) {

return segmentArr[node] = arr[left];
}

int mid = (left + right) / 2;

return segmentArr[node] = Math.min(init(arr, left, mid, node * 2), init(arr, mid + 1, right, node * 2 + 1));
}

// func#1. 구간 최소값 query
int query(int left, int right, int node, int nodeLeft, int nodeRight) {

// 두 구간이 겹치지 않는 경우
if (left > nodeRight || right < nodeLeft) return MAX_VALUE;

// 노드 구간이 완전히 속하는 경우
if (left <= nodeLeft && right >= nodeRight) return segmentArr[node];

// 그 외의 경우
int nodeMid = (nodeLeft + nodeRight) / 2;

return Math.min(query(left, right, node * 2, nodeLeft, nodeMid), query(left, right, node * 2 + 1, nodeMid + 1, nodeRight));
}

// func#2. SegmentTree 노드 값 갱신
int update(int index, int newValue, int node, int nodeLeft, int nodeRight) {

// 노드 구간에 포함되지 않는 경우
if (index < nodeLeft || index > nodeRight) return segmentArr[node];

// 노드 구간에 포함되는 경우
// Leaf인 경우
if (nodeLeft == nodeRight) return segmentArr[node] = newValue;

int nodeMid = (nodeLeft + nodeRight) / 2;

// Leaf가 아닌 경우
return segmentArr[node] = Math.min(update(index, newValue, node * 2, nodeLeft, nodeMid), update(index, newValue, node * 2 + 1, nodeMid + 1, nodeRight));
}

}

(Kotlin), 
Kotlin은 좀 더 공부한 이후에 
더 나은 방향으로 코드를 고쳐보겠습니다.
import java.util.*

fun main(args: Array<String>) {

// input
val input = Scanner(System.`in`)

val N = input.nextInt()

val arr = IntArray(N, { 0 })

for (i in 0..N - 1) {

arr[i] = input.nextInt()
}

// solve
val segmentTree = SegmentTree(arr, N)

var nQuery = input.nextInt()

while (nQuery-- > 0) {

val queryType = input.nextInt()

if (queryType == 1) {

val i = input.nextInt() - 1
val v = input.nextInt()

segmentTree.update(i, v, 1, 0, N - 1)

} else {

val i = input.nextInt() - 1
val j = input.nextInt() - 1

println(segmentTree.query(i, j, 1, 0, N - 1))

}
}
}

class SegmentTree(arr: IntArray, n: Int) {

val segmentArr: IntArray
val MAX_VALUE = Integer.MAX_VALUE

init {

val x = Math.ceil((Math.log(n.toDouble()) / Math.log(2.toDouble())))

val segmentSize = (Math.pow(2.toDouble(), x) * 2).toInt()

segmentArr = IntArray(segmentSize, { 0 })

initialize(arr, 0, n - 1, 1)

}

fun initialize(arr: IntArray, left: Int, right: Int, nodeIdx: Int): Int {

if (left == right) {

segmentArr[nodeIdx] = arr[left]
return segmentArr[nodeIdx]
}

val mid = (left + right) / 2

segmentArr[nodeIdx] = Math.min(initialize(arr, left, mid, nodeIdx * 2), initialize(arr, mid + 1, right, nodeIdx * 2 + 1))

return segmentArr[nodeIdx]
}

fun query(left: Int, right: Int, nodeIdx: Int, nodeLeft: Int, nodeRight: Int): Int {

if (left > nodeRight || right < nodeLeft) return MAX_VALUE

if (left <= nodeLeft && right >= nodeRight) return segmentArr[nodeIdx]

val nodeMid = (nodeLeft + nodeRight) / 2

return Math.min(query(left, right, nodeIdx * 2, nodeLeft, nodeMid), query(left, right, nodeIdx * 2 + 1, nodeMid + 1, nodeRight))
}

fun update(index: Int, newValue: Int, nodeIdx: Int, nodeLeft: Int, nodeRight: Int): Int {

if (index < nodeLeft || index > nodeRight) return segmentArr[nodeIdx]

if (nodeLeft == nodeRight) {

segmentArr[nodeIdx] = newValue

return segmentArr[nodeIdx]
}

val nodeMid = (nodeLeft + nodeRight) / 2

segmentArr[nodeIdx] = Math.min(update(index, newValue, nodeIdx * 2, nodeLeft, nodeMid), update(index, newValue, nodeIdx * 2 + 1, nodeMid + 1, nodeRight))

return segmentArr[nodeIdx]
}

}


'Algorithm > 세그먼트 트리' 카테고리의 다른 글

BOJ#3653 영화 수집  (0) 2017.10.11
BOJ#6549 히스토그램에서 가장 큰 직사각형  (0) 2017.08.30
BOJ#2042 구간 합 구하기  (0) 2017.05.26