이 문제의 핵심은 SubTree의 크기를 구해야되는 쿼리가 10^5번 들어올 수 있기 때문에, 처음에 Root부터 시작해서 모든 노드의 SubTree의 크기를 구해둬야한다. ( 그렇게 안해도 시간 초과가 안나오는지는 모르겠습니다! ) 쿼리마다 구하면 아마 시간초과가 나올 것 같다.
알고리즘
TreeDp를 이용해서 Root 부터 시작해서 재귀적으로 모든 노드들의 SubTree 의 크기를 구해야한다. 현재 노드가 연결된 노드들이 이미 다 방문한 노드라면, 해당 노드는 leaf 노드 이므로 Dp 값은 0이고, 1을 return 해주면 된다. leaf 노드가 아니라면 자식 노드들의 subTree 값을 모두 더해서 저장하고, 자기 자신을 포함해서 subTree + 1 값을 return 해주면 된다.
분명, 자바의 모든 객체는 Object 를 상속한다고 했습니다. 클래스나 메소드를 Object 자료형으로 만든다면, 자료형에 얽매이지않는 클래스나 메소드를 만들 수 있지 않을까요?? 물론, Object를 이용해서 제네릭 문법을 활용한 클래스와 동일한 작업을 하도록 구현 할 수 있습니다.
그렇다면 제네릭 클래스를 사용하면 무슨 이점이 있을까요??
똑같은 작업을 제네릭클래스와 Object를 이용한 클래스로 각각 구현해나가며 알아보겠습니다.
1) 과일을 담을 수 있는 Box를 만든다.
2) Box에 어떤 과일을 담는다.
3) Box에 담긴 과일을 꺼낸다.
먼저, 일반적인 클래스를 정의하고 이를 이용해 Box 에 Apple 을 담았다가 꺼내보겠습니다.
자바의 모든 객체는 Object를 상속한다는 점을 이용해 Object를 담는 Box를 만들었습니다. 그 후, Apple을 Box에 담았다가, Box에서 get 메소드를 이용해 꺼내보았습니다.
우리는 Box에 어떤 값도 넣을 수 있고, 어떤 값도 꺼낼 수 있기를 원하기 때문에 Object로 잘 구현했습니다. 그럼, 이 코드의 불편한 점은 무엇일까요?? 바로 get 메소드를 이용할 때 형변환을 항상 해야된다는 점입니다! 우리는 Box에 Apple 클래스를 넣을 수도 있고, Melon 클래스를 넣을 수도 있습니다. 따라서 get 메소드에서는 Object를 return 해주게되고, 우리가 원하는 자료형으로 다시 형 변환을 해야 한다는 불편함이 있습니다.
우리는 Box 클래스를 정의할 때, "과일을 담는 박스" 라고 정의했습니다. 하지만, 지금 "Apple" 이라는 문자열을 Box에 저장하고 get() 메소드를 이용하고 있습니다. 문법적으로 문제가 있을까요?? String 도 Object 를 상속하기 때문에 개발자의 의도와 전혀 다르게 Box가 사용되고 있지만 컴파일 단계에서 문제가 드러나지 않습니다.
제니릭 클래스를 매개변수로 받아 안에 있는 내용물을 return 해주는 open 제네릭 메소드를 만들었습니다. 우리가 기존에 알던 메소드와 구조적으로 크게 다른 점은 없으나 "자료형" 을 일반화 시킬 수 있습니다. 즉, 박스에 어떤 자료형이 담겨있어도 꺼내올 수 있는 메소드입니다.
BigInteger 클래스는 클래스 이름 그대로, 큰! 정수를 다루기 위해 등장한 클래스입니다. 우리는 정수를 int, long 기본 자료형으로 표현할 수 있습니다. 하지만, int, long 은 모두 표현할 수 있는 값의 크기가 한계가 있습니다. 그러면, long 형 범위보다도 큰 값을 JAVA 에서는 표현할 수 없을까요?
1 ~ N 까지 좌표들이 있고, 오리 두마리가 A 와 B 지점에서 시작해서 하루에 한 번 점프를 해가며 이동한다. 점프를 안하는 경우는 없고, 점프 크기는 1부터 시작해서 2배씩 늘어난다. 이때, 둘이 만날 수 있는지, 만난다면 몇 일이 지나고 만나는지를 구하는 프로그램을 작성하라.
문제핵심
1. 점프 크기가 2배씩 늘어나므로 19일에 점프크기는 262,144(2^18)가 되고 N이 최대 500,000이므로 20일부터는 의미가 없어진다. 왜냐하면 20일 부터 점프 크기가 500,000보다 크므로 점프를 하면 어차피 범위를 벗어나게 된다. 따라서, 우리는 19일까지만 시뮬레이션을 진행해보면 된다. --> 19 * O(N) 으로 처리하자.
2. 우리는 k 일에 A 오리와 B 오리가 갈 수 있는 모든 지점을 알고 있다면, (k + 1) 일에 A 오리와 B 오리가 갈 수 있는 모든 지점을 구할 수 있다. --> 모든 지점을 저장해가면서 진행해보자.
알고리즘
문제핵심 1, 2번에 의해서 현재 A, B 오리가 도착한 지점을 통해 다음 A, B 오리가 도착한 지점을 저장해나가며 문제를 해결할 수 있다. 메모리를 조금이라도 아끼기위해 bool 배열과 flag 기법을 이용해서 저장해나갔다.
day 0부터 시작해서 day가 짝수라면 visited[0] 에 현재 방문한 지점들이 있고, visited[1] 에 다음에 방문할 지점들을 check 해가며 문제를 해결했다.
1) 현재 방문한 지점들 중에 겹치는 곳이 있는지. --> 있다면 오리가 만났다!
2) 다음에 방문한 지점을 저장할 공간을 초기화한다.
3) 현재 지점을 토대로 다음에 방문할 지점을 계산해 저장한다.
알고리즘회고
처음에는 2개의 deque를 이용해서 현재 A 오리가 갈 수 있는 모든 지점을 저장하고, B 오리가 갈 수 있는 모든 지점을 저장하고, deque의 앞에서부터 현재 지점들을 뽑아가며 jump 를 +, jump 를 - 해서 다음 지점들을 다시 deque의 뒤로 push 했다. 당연히 deque의 push, pop 이 굉장히 많이 일어났고, 현재 지점들에서 같은 지점이 있는지 없는지 체크를 위해 2개의 deque을 2중 for문으로 순회해야되서 시간초과를 맛보게 되었다. 따라서, 메모리를 조금 더 사용하고 deque을 사용하지 않는 방식으로 알고리즘을 갈아엎었다.
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
#include<iostream>
#include<qcueue>
#include<vector>
usingnamespacestd;
int N, A, B, jump =1, day =0;
bool visitedA[2][500001] = {};
bool visitedB[2][500001] = {};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
// 19일 안에 만나야됨!
cin>> N >> A >> B;
visitedA[0][A] =1;
visitedB[0][B] =1;
do {
// day 가 짝수라면 visited[0] 에 기존것이있음!
// day 가 홀수라면 visited[1] 에 기존것이있음!
int today = day % 2;
int nextDay = (today +1) % 2;
// 같은게 있는지 찾아보자.
bool flag =false;
for (int i =1; i <= N; i++) {
if (visitedA[today][i] && visitedB[today][i]) {
flag =true;
break;
}
}
if (flag) {
cout<< day <<'\n';
break;
}
// 초기화 진행
for (int i =1; i <= N; i++){
visitedA[nextDay][i] =0;
visitedB[nextDay][i] =0;
}
for (int i =1; i <= N; i++) {
int nextJump1 = i + jump, nextJump2 = i - jump;
if (visitedA[today][i]) {
if (1<= nextJump1 && nextJump1 <= N) visitedA[nextDay][nextJump1] =1;
if (1<= nextJump2 && nextJump2 <= N) visitedA[nextDay][nextJump2] =1;
}
if (visitedB[today][i]) {
if (1<= nextJump1 && nextJump1 <= N) visitedB[nextDay][nextJump1] =1;
if (1<= nextJump2 && nextJump2 <= N) visitedB[nextDay][nextJump2] =1;