문제링크 입니다

🚀 [INSERTION] 문제 😊😊😊😭😭

유명한 정렬 알고리즘인 삽입 정렬은 정렬된 부분 배열을 유지하며 이 배열에 새 원소를 삽입해 나가는 식으로 동작합니다. 주어진 정수 배열 A를 정렬하는 삽입 정렬의 구현은 다음과 같습니다.

void insertionSort(vector<int>& A) {
  for(int i = 0; i < A.size(); ++i) {
    // A[0..i-1] 에 A[i] 를 끼워넣는다
    int j = i;
    while(j > 0 && A[j-1] > A[j]) {
      // 불변식 a: A[j+1..i] 의 모든 원소는 A[j] 보다 크다.
      // 불변식 b: A[0..i] 구간은 A[j] 를 제외하면 정렬되어 있다.
      swap(A[j-1], A[j]);
      --j;
    }
  }
}

삽입 정렬은 A[0..i-1] 이 정렬된 배열일 때, A[i] 를 적절한 위치를 만날 때까지 왼쪽으로 한칸씩 움직입니다. 예를 들어 A={5,1,4,3,2} 의 삽입 정렬은 다음과 같이 이루어집니다.

A 비고
51432 초기상태
15432 72
14532 4를 왼쪽으로 1칸 옮김
13452 3을 왼쪽으로 2칸 옮김
12345 2를 왼쪽으로 3칸 옮김

1부터 N까지의 자연수가 한 번씩 포함된 길이 N 인 수열 A[] 를 삽입 정렬했습니다. 원래 수열은 알 수 없지만, 그 과정에서 각 원소가 왼쪽으로 몇 칸이나 이동했는지를 알고 있습니다. 예를 들어, 위 예제에서 각 위치에 있는 값들이 움직인 칸수를 표현하면 {0,1,1,2,3} 이 됩니다. 이 때 원래 수열을 찾아내는 프로그램을 작성하세요.

🔑 [풀이]

뒤에서부터 문제를 풀면 쉽게 해결할 수 있는 문제였습니다.

마지막 숫자 A[N-1]이 왼쪽으로 몇 칸 움직였는지를 보면 A[N-1]에 어떤 숫자가 들어가야 하는지 알 수 있습니다.

이것들을 계산하기 위해서는 A[i] 에 들어갈 숫자들을 어떻게 저장 할 것인가, 후보들 중 k번째 원소를 찾고, 삭제하는 작업을 빠르게 해야 하기 떄문에 이진검색 트리를 이용합니다.
하지만 set과 map은 원소를 찾는 기능을 제공하지 않기 때문에, 이전에 배웠던 트립을 사용하여 문제를 해결합니다.

⌨️ 입력

입력의 첫 줄에는 테스트 케이스의 수 C (1 <= C <= 50) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 원 배열의 길이 N (1 <= N <= 50000) 이 주어집니다. 그 다음 줄에 N 개의 정수로 A의 각 위치에 있던 값들이 움직인 칸수가 주어집니다. A 는 1부터 N 까지의 정수를 한 번씩 포함합니다.

입력의 양이 많으므로 가능한 빠른 입력 함수를 사용하는 것이 좋습니다.

🖥 출력

각 테스트 케이스마다 재구성한 A[] 를 한 줄에 출력합니다.

🖱 입력 예제

2
5
0 1 1 2 3
4
0 1 2 3

💻 출력 예제

5 1 4 3 2
4 3 2 1

💎 전체 코드는 다음과 같습니다.

//
//  INSERTION.cpp
//  AALGGO
//
//  Created by inhyeok on 2021/12/22.
//

#include <iostream>
#include <fstream>

using namespace std;
ifstream fin("INSERTION.txt");
int N;
const int MAXN = 50000;
int A[MAXN];
int shifted[MAXN];

struct Node{
    int key;
    int priority, size;
    Node *left, *right;
    Node(const int& _key) : key(_key), priority(rand()), size(1), left(NULL), right(NULL) {}
    void setLeft(Node *newLeft){
        left = newLeft;
        calcSize();
    }
    void setRight(Node *newRight){
        right = newRight;
        calcSize();
    }
    void calcSize(){
        size = 1;
        if(left) size += left->size;
        if(right) size += right->size;
    }
};
typedef pair<Node*,Node*> NodePair;
NodePair split(Node* root, int key){
    if(root == NULL) return NodePair(NULL,NULL);
    if(root->key < key){
        NodePair rs = split(root->right,key);
        root->setRight(rs.first);
        return NodePair(root,rs.second);
    }
    NodePair ls = split(root->left,key);
    root->setLeft(ls.second);
    return NodePair(ls.first,root);
}
Node* insert(Node* root, Node* node){
    if(root == NULL) return node;
    if(node->priority > root->priority){
        NodePair splitted = split(root, node->key);
        node->setLeft(splitted.first);
        node->setRight(splitted.second);
        return node;
    }
    else if(node->key < root->key){
        root->setLeft(insert(root->left,node));
    }
    else{
        root->setRight(insert(root->right,node));
    }
    return root;

}

// A<B 일떄
Node* merge(Node* A, Node* B){
    if(A == NULL) return B;
    if(B == NULL) return A;
    if(B->priority > A->priority){
        B->setLeft(merge(A,B->left));
        return B;
    }
    A->setRight(merge(A->right, B));
    return A;
}

Node* erase(Node *root, int key){
    if(root == NULL) return root;
    if(root->key == key){
        Node* ret = merge(root->left, root->right);
        delete root;
        return ret;
    }
    else if(key< root->key){
        root->setLeft(erase(root->left, key));
    }

    else {
        root->setRight(erase(root->right, key));
    }

    return root;
}
// root 노드에서 k 번째 원소를 찾을떄
Node* kth(Node* root, int k){
    int leftSize = 0;
    if(root->left != NULL) leftSize = root->left->size;
    if(k <= leftSize) return kth(root->left, k);
    if(k == leftSize+1 ) return root;
    return kth(root->right,k - leftSize - 1);
}
void solve(){
    Node* candidates = NULL;
    for(int i=0; i<N; i++){
        candidates = insert(candidates, new Node(i+1));
    }
    for(int i=N-1; i>=0; i--){
        Node* k = kth(candidates, i - shifted[i] + 1 );
        A[i] = k->key;
        candidates = erase(candidates,k->key);
    }
}

int main(){
    int test_case;
    fin >> test_case;
    for(int test=0; test<test_case; test++){
        fin >> N;
        for(int i=0; i<N; i++){
            fin >> shifted[i];
        }
        solve();
        for(int i=0; i<N; i++){
            cout << A[i] << " ";
        }
        cout << endl;
    }
}

INSERTION

Comments