백준: Class 4 - 1932, 1991, 5639

4 분 소요


</br> 클래스 4 계속
</br>

1932: 정수 삼각형

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

방법 1.

void func(int ix, int c){
    if( ix >= sz-n ){
        ans = max(ans, c);
        return;
    }
    func(ix+ind[ix], c + heap[ix+ind[ix]]);
    func(ix+ind[ix]+1, c + heap[ix+ind[ix]+1]);
}

// in main()
    sz = n*(n+1)/2;
    for(int i = 0, j = 1; i < sz; j++){
        int k = j*(j+1)/2;
        while( i < k ){
            ind[i] = j;
            i++;
        }
    }
    for(int i = 0; i < sz; i++){
        cin >> heap[i];
    }
    func(0, heap[0]);
    cout << ans << endl;

아하… 힙 모양이네 막 min heap 이런 거 할 때 배열 형태로 저장했었지
나도 그렇게 저장해서 인덱스 규칙 따라서 dfs로 찾아 보자

했는데 시간 초과 났다
</br>

헉… 왜 그럴까 막 그러다보니 다른 방법이 생각났는데

방법 2.

    for(int i = 0; i < n; i++){
        for(int j = 0; j < i+1; j++){
            cin >> heap[i][j];
        }
    }
    for(int i = 0; i < n; i++){
        dp[n-1][i] = heap[n-1][i];
    }
    for(int i = n-1; i >= 0; i--){
        for(int j = 0; j < i+1; j++){
            dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]);
            dp[i][j] += heap[i][j];
        }
    }
    cout << dp[0][0] << endl;

거꾸로 올라가면 선택할 필요가 없다!! dp문제였다

ex) n = 3
      0 0
   1 0   1 1
2 0   2 1   2 2

삼각형의 인덱스가 이런 모양이라면 (1, 0)에서의 최댓값은 (2, 0)과 (2, 1) 중 최댓값을 골라 힙의 값을 더한 것일 것이다.
</br>

1991: 트리 순회

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

#include <bits/stdc++.h>
using namespace std;
tuple<char, char, char> tree[27];

void preorder(int now){
    cout << get<0>(tree[now]);
    if( get<1>(tree[now]) != '.' ) preorder(get<1>(tree[now])-'A');
    if( get<2>(tree[now]) != '.' ) preorder(get<2>(tree[now])-'A');
}
void inorder(int now){
    if( get<1>(tree[now]) != '.' ) inorder(get<1>(tree[now])-'A');
    cout << get<0>(tree[now]);
    if( get<2>(tree[now]) != '.' ) inorder(get<2>(tree[now])-'A');
}
void postorder(int now){
    if( get<1>(tree[now]) != '.' ) postorder(get<1>(tree[now])-'A');
    if( get<2>(tree[now]) != '.' ) postorder(get<2>(tree[now])-'A');
    cout << get<0>(tree[now]);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int n;
    cin >> n;
    for(int i = 0; i < n; i++){
        char p, c1, c2;
        cin >> p >> c1 >> c2;
        tree[p-'A'] = make_tuple(p, c1, c2);
    }
    preorder(0);
    cout << '\n';
    inorder(0);
    cout << '\n';
    postorder(0);
    cout << '\n';
}

트리… 작년 자료구조 때 struct로 만들고 막 그랬던
지금은 좀 귀찮아서 그냥 튜플로 처리
전위는 루트-왼-오, 중위는 왼-루트-오, 후위는 왼-오-루트이므로, 출력 위치만 루트 자리로 바꿔 주면 된다

지금 보니까 이 문제는 상관없지만 다른 문제들은 인덱스가 자기 마음대로일 수 있으므로, map< char, pair<char, char> >이런 식으로 선언하는 게 더 나을 것 같다
</br>

5639: 이진 검색 트리

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

방법 1.

#include <bits/stdc++.h>

using namespace std;

map<int, pair<int, int> > tree;

void postorder(int now){
    if( tree[now].first != 0 ) postorder(tree[now].first);
    if( tree[now].second != 0 ) postorder(tree[now].second);
    cout << now << '\n';
}

void addNode(int r, int k){
    if( k < r ){
        if( tree[r].first ) addNode(tree[r].first, k);
        else{
            tree[r].first = k;
            return;
        }
    }
    else{
        if( tree[r].second ) addNode(tree[r].second, k);
        else{
            tree[r].second = k;
            return;
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    vector<int> v;
    int a, root = 0;
    while( cin >> a ){
        tree[a] = make_pair(0, 0);
        if( root == 0 ) root = a;
        else addNode(root, a);
    }
    postorder(root);
}

입력 받을 때마다 노드를 만들고, 루트에서부터 찾아 이진 탐색에 따라 삽입하기를 했다.
따라서 트리가 복구되고, 이후 위에도 쓴 postorder() 함수를 사용한다.

근데 시간초과 난다!!
헉… 어째설까
그래서 질문글을 올렸었는데, 친절하신 분이 바로 답을 달아주셨다
https://www.acmicpc.net/board/view/73500#post

Q) 트리에 노드 입력은 평균 O(logN)이고, 최악의 경우 O(N)이라고 알고 있습니다. 따라서 위 코드는 복잡도가 최악에 O(N^2)이 아닌가요?
A) map은 원소를 삽입, 삭제, 탐색하는 과정에서 O(logN)이 걸립니다. 이때문에 실제 시간복잡도는 질문자님이 예상하신 것에 logN이 붙어 O(N^2 logN)이 됩니다.

내가 바보였구나
귀찮다고 맵으로 만들어 놓고ㅜㅜ
</br>

방법 2.

#include <bits/stdc++.h>

using namespace std;

vector<int> pre, in;
int d;

void post(int b, int e){
    int root = -1;
    for(int i = b; i < e; i++){
        if( in[i] == pre[d] ){
            root = i;
            break;
        }
    }
    d++;
    if( root != b ) post(b, root);
    if( root != e-1 ) post(root+1, e);
    cout << in[root] << endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int a;
    while( cin >> a ){
        pre.push_back(a);
        in.push_back(a);
    }
    sort(in.begin(), in.end());
    post(0, pre.size());
}

그래서, 자료구조 시간 때 전위, 중위로 후위 구하기, 중위, 후위로 전위 구하기 등? 했던 게 생각나서 그렇게 풀어 보기로 했다.
이진 탐색이니까 중위순회는 정렬된 모습이다.

ex) 백준 예제
pre: 50 30 24 5 28 45 98 52 60
in : 5 24 28 30 45 50 52 60 98

전위순회는 루트 우선이므로, 중위순회에서 각각 루트의 위치를 알면 다 풀린다.

pre: 50   30 24 5 28 45 98 52 60
in : 5 24 28 30 45   <50>   52 60 98

pre: 50 30    24 5 28 45 98 52 60
in : 5 24 28   <30>   45

pre: 50 30 24    5 28 45 98 52 60
in : 5   <24>   28

pre: 50 30 24 5    28 45 98 52 60
in : <5>

pre: 50 30 24 5 28    45 98 52 60
in : <28>

pre: 50 30 24 5 28 45    98 52 60
in : <45>

pre: 50 30 24 5 28 45 98    52 60
in : 52 60   <98>

pre: 50 30 24 5 28 45 98 52   60
in : <52>   60

pre: 50 30 24 5 28 45 98 52   60
in : <60>

대충 함수의 실행 순서는 이렇고, 루트 출력을 가장 마지막에 해 주면 후위순회가 가능하다(왼쪽>오른쪽>루트).
</br>


오랜만에 트리를 이렇게 잔뜩…
추억에 잠겼다
</br>

댓글남기기