C++ 코딩테스트 강좌, 집합 표현하기

코딩테스트에서 자주 사용되는 집합 표현과 관련된 알고리즘 문제를 다루어 볼 것입니다. 집합을 효과적으로 표현하고 다리기 위해서는 다양한 데이터 구조와 알고리즘의 이해가 필수적입니다. 이번 글에서는 집합을 표현하고, 연산을 수행하는 알고리즘 문제를 하나 제시하고 이를 단계별로 해결해보도록 하겠습니다.

문제: 두 집합의 교집합

주어진 두 수의 집합에서 공통으로 포함된 원소들의 집합을 구하는 문제입니다. 이는 기본적인 집합 연산 중 하나이며, C++ STL을 활용하여 간단히 해결할 수 있습니다.

문제 정의


입력:
 - 두 정수 N, M (1 ≤ N, M ≤ 100,000): 첫 번째 집합의 원소 개수 N, 두 번째 집합의 원소 개수 M
 - 첫 번째 집합의 원소: N개의 정수 a1, a2, ..., aN
 - 두 번째 집합의 원소: M개의 정수 b1, b2, ..., bM

출력:
 - 두 집합의 교집합의 원소를 오름차순으로 출력하라.
    

예시


입력:
5 4
1 2 3 4 5
3 4 5 6

출력:
3 4 5
    

문제 풀이 과정

1단계: 문제 분석

주어진 두 집합에서 교집합을 찾기 위해서는 두 집합의 원소를 비교하여 공통으로 존재하는 원소를 찾아야 합니다. 이를 위해 해시셋(집합) 자료구조를 사용할 수 있으며, C++의 STL에서는 std::unordered_set를 제공하므로 이를 활용할 수 있습니다.

2단계: 알고리즘 설계

알고리즘은 다음과 같이 설계할 수 있습니다.

  1. 두 집합을 각각 입력받는다.
  2. 첫 번째 집합의 원소들을 해시셋에 저장한다.
  3. 두 번째 집합의 각 원소를 순회하면서 첫 번째 집합의 해시셋에 존재하는지 확인한다.
  4. 존재한다면 교집합 원소 리스트에 추가한다.
  5. 결과를 오름차순으로 정렬한 후 출력한다.

3단계: C++ 코드 구현

위의 알고리즘을 바탕으로 C++ 코드를 작성해보겠습니다.


#include <iostream>
#include <unordered_set>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int N, M;
    cin >> N >> M;

    unordered_set setA;
    vector intersection;

    // 첫 번째 집합 입력받기
    for (int i = 0; i < N; i++) {
        int a;
        cin >> a;
        setA.insert(a);
    }

    // 두 번째 집합 입력받기 및 교집합 찾기
    for (int i = 0; i < M; i++) {
        int b;
        cin >> b;
        // setA에 b가 존재하면 intersection에 추가
        if (setA.find(b) != setA.end()) {
            intersection.push_back(b);
        }
    }

    // 교집합 원소 정렬
    sort(intersection.begin(), intersection.end());

    // 결과 출력
    for (int num : intersection) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}
    

4단계: 코드 설명

이 코드에서는 다음과 같은 작업을 수행합니다:

  • unordered_set setA;: 첫 번째 집합의 원소를 저장하기 위한 unordered_set을 선언합니다.
  • vector intersection;: 교집합 원소를 저장하기 위한 벡터를 선언합니다.
  • 첫 번째 집합의 원소를 입력받아 setA.insert(a);를 통해 해시셋에 추가합니다.
  • 두 번째 집합의 각 원소를 순회하면서 setA.find(b)를 통해 첫 번째 집합에 존재하는지 확인하고, 존재하면 교집합 배열에 추가합니다.
  • 마지막으로 결과를 정렬하고 출력합니다.

5단계: 복잡도 분석

시간 복잡도 측면에서 봤을 때, 첫 번째 집합의 원소를 해시셋에 추가하는 과정은 O(N)이고, 두 번째 집합의 각 원소에 대해 해시셋을 검색하는 과정은 O(M)입니다. 따라서 전체 알고리즘의 시간 복잡도는 O(N + M)입니다. 메모리 복잡도는 해시셋과 교집합 저장을 위한 메모리 사용을 고려할 때 O(N + min(N, M))입니다.

정리 및 결론

이번 포스팅에서는 집합을 표현하고 교집합을 계산하는 문제를 다루어 보았습니다. C++의 STL을 활용하여 간단히 구현할 수 있었으며, 알고리즘의 시간 복잡도 또한 효율적인 것을 확인할 수 있었습니다. 이러한 방식으로 집합 관련 문제를 효과적으로 해결하기 위해서는 적절한 자료구조를 선택하는 것이 중요합니다.

추가적인 학습을 위해 다양한 연습문제와 함께 집합의 다른 연산들(합집합, 차집합 등)에 대해서도 학습해보시기를 추천드립니다. 감사합니다!

C++ 코딩테스트 강좌, 조합 알아보기

안녕하세요, 여러분! 이번 시간에는 C++를 이용한 코딩테스트 준비에 대해 이야기해 보겠습니다. 특히, 조합(Combination) 알고리즘에 대해 깊이 있는 이해를 돕기 위해 문제를 하나 풀어보도록 하겠습니다. 조합은 특정 조건을 충족하는 항목들을 선택하는 방법을 찾는데 매우 유용한 알고리즘입니다.

조합의 정의

조합은 주어진 요소들 중에서 특정 개수의 요소를 선택하는 방법을 의미합니다. 예를 들어, 집합 {A, B, C}에서 2개를 선택하는 경우는 {A, B}, {A, C}, {B, C}의 3가지가 됩니다. 여기서 중요한 점은 선택된 요소들의 순서가 중요하지 않다는 것입니다. 이는 우리가 순열(Permutation)과 구별되는 중요한 차이점입니다.

문제 설명

문제: 조합의 합

주어진 정수 배열 nums와 정수 target가 있을 때, target의 합을 만들 수 있는 조합을 모두 찾아서 출력하시오. 조합에서 각 숫자는 여러 번 사용될 수 있으며, 같은 숫자가 있는 조합은 여러 번 포함하지 않아야 합니다.

입력 예시

nums = [2, 3, 6, 7]
target = 7

출력 예시

[[2, 2, 3], [7]]

문제 풀이 과정

1. 문제 이해하기

문제를 해결하기 위해 우리는 백트래킹(Backtracking) 알고리즘을 사용할 것입니다. 백트래킹은 문제를 해결하는 과정에서 가능한 선택을 탐색하고, 만약 그 선택이 유효하지 않거나 최적 해를 찾지 못하면 이전 단계로 돌아가서 다른 선택을 시도하는 접근 방식입니다.

2. 알고리즘 설계

이번 문제에 대한 알고리즘은 다음과 같은 단계로 이루어집니다.

  1. 현재의 조합을 배열에 추가합니다.
  2. 조합의 합이 target과 같아지면 해당 조합을 결과 배열에 추가합니다.
  3. 현재의 조합이 target보다 크면 재귀 호출을 종료합니다.
  4. 조합에 대한 가능한 숫자를 추가하고, 같은 숫자를 선택할 수 있도록 재귀 호출을 합니다.
  5. 마지막으로 선택한 숫자를 다시 제거하여 이전 상태로 돌아갑니다.

3. 코드 구현하기

이제 C++로 구현한 코드를 보겠습니다.

#include <iostream>
#include <vector>
using namespace std;

void backtrack(vector<int> &combination, vector<vector<int>> &result, vector<int> &nums, int target, int start) {
    if (target == 0) {
        result.push_back(combination);
        return;
    }
    
    for (int i = start; i < nums.size(); i++) {
        if (nums[i] > target) continue; // 현재 숫자가 target보다 크면 건너뛴다
        combination.push_back(nums[i]); // 조합에 숫자를 추가
        backtrack(combination, result, nums, target - nums[i], i); // 재귀 호출
        combination.pop_back(); // 마지막 숫자를 제거하여 이전 상태로 돌아간다
    }
}

vector<vector<int>> combinationSum(vector<int> &nums, int target) {
    vector<vector<int>> result;
    vector<int> combination;
    backtrack(combination, result, nums, target, 0);
    return result;
}

int main() {
    vector<int> nums = {2, 3, 6, 7};
    int target = 7;
    vector<vector<int>> result = combinationSum(nums, target);
    
    for (const auto &comb : result) {
        cout << "[ ";
        for (int num : comb) {
            cout << num << " ";
        }
        cout << "]" << endl;
    }
    
    return 0;
}

4. 코드 설명

위 코드는 combinationSum라는 함수가 주어진 숫자 배열과 타겟 값을 입력받아 가능한 모든 조합의 배열을 반환합니다. 함수 내부에서는 backtrack라는 재귀 함수를 호출하여 조합을 생성합니다.

  • backtrack 함수: 이 함수는 현재 조합을 배열에 담고, 타겟 값을 갱신하며, 재귀적으로 반복하여 새로운 조합을 탐색합니다.
  • 조건문: 타겟이 0일 때 현재 조합을 결과 배열에 추가하고, 타겟이 현재 숫자보다 작을 경우 탐색을 종료합니다.
  • 조합 추가: 현재 숫자를 조합에 추가한 후, 타겟 값을 줄이고 재귀 호출을 진행합니다.
  • 조합 제거: 백트래킹을 위해 조합에서 마지막 숫자를 제거하여 이전 상태로 돌아갑니다.

5. 시간 복잡도 분석

이 문제의 시간 복잡도는 최악의 경우 조합의 수에 비례합니다. 따라서, 입력의 크기와 가짓 수에 따라 매우 비효율적일 수 있습니다. 그러나 유용하게 사용될 수 있는 상황이 많으므로 이러한 알고리즘에 대해 잘 이해하고 연습하는 것이 중요합니다.

6. 결론

이번 강좌를 통해 C++에서의 조합 문제를 이해하고 풀이하는 과정을 배웠습니다. 조합 문제는 여러 문제에 활용될 수 있으므로, 주어진 상황을 잘 이해하고 적절한 알고리즘을 선택하는 것이 중요합니다. 다양한 연습 문제들을 통해 코딩 능력을 더욱 향상시킬 수 있기를 바랍니다.

추가 예제 및 연습

아래의 추가 예제를 풀어보며 더욱 다양한 조합 문제를 연습해보세요:

  • 예제 1: nums = [1, 2, 3], target = 4인 경우, 가능한 조합은?
  • 예제 2: nums = [3, 5, 7], target = 8인 경우, 가능한 조합은?

코딩 능력을 키우기 위해 지속적으로 연습하는 것이 중요합니다. 다양한 문제를 풀어나가며 경험을 쌓으시길 바랍니다!

참고 자료

자세한 알고리즘과 C++에 대한 심화 내용을 원하신다면 아래의 자료를 참고해주세요:

여러분의 코딩 공부를 응원합니다!

C++ 코딩테스트 강좌, 주몽의 명령

문제 설명

주몽의 명령은 알고리즘 문제 중 하나로, 주몽이 군사 작전 중 겪는 상황을 기반으로 한 문제입니다.
주몽은 자신의 부대에 속한 병사들에게 특정 명령을 내리려고 합니다. 하지만 병사들은 주몽의 명령을 정확히 이해하지 못할 수도 있습니다.
이러한 상황을 고려하여, 아래와 같은 문제를 해결해야 합니다.

문제 정의

        주몽은 N명의 병사에게 명령을 내리려고 합니다. 각 병사는 정확히 한 번 명령을 받을 수 있으며, 이 명령은 
        다음과 같은 형식으로 이루어집니다. 각 병사는 1부터 N까지 번호가 매겨져 있습니다.

        명령: `i j k`
        
        - 의미: 저 앞에 있는 i번 병사부터 j번 병사까지는 명령을 따라야 하고,
        - k번 병사는 이를 무시할 수 있습니다.

        만약 명령을 따르는 병사의 수가 k번 병사보다 많으면 "YES"를 출력하고, 
        그렇지 않으면 "NO"를 출력하세요.

         N, M
         i, j, k
    

풀이 과정

이 문제를 해결하기 위해서는 다음과 같은 단계로 접근할 수 있습니다.

1단계: 문제 이해 및 분석

문제를 해결하기 위해 병사들의 번호와 해당 명령을 시행할 범위가 주어집니다.
주어진 i와 j는 명령을 따르는 병사들의 범위를 나타내며, k는 이 범위에 포함되어 있지만 명령을 따르지 않을
특정 병사를 나타냅니다. 따라서 병사 i부터 j까지의 병사 수와 k번 병사 여부를 체크하여 “YES” 또는 “NO”를 출력하면 됩니다.

2단계: 알고리즘 설계

다음의 알고리즘을 사용하여 문제를 해결할 수 있습니다.

  1. N과 M을 입력받습니다.
  2. 명령을 M번 입력받습니다.
  3. 각 명령에 대해 다음과 같은 작업을 수행합니다.
    • 명령의 범위 [i, j]에서 병사의 수를 계산합니다.
    • k번 병사가 범위에 포함되는지 확인합니다.
    • 명령을 따르는 병사의 수가 k번 병사보다 많으면 “YES”를 출력, 아니면 “NO”를 출력합니다.

3단계: C++ 코드 구현

이제 위에서 설계한 알고리즘을 C++ 코드로 구현하겠습니다.

        #include 
        using namespace std;

        int main() {
            int N, M;
            cin >> N >> M; // 병사 수, 명령 수 입력 받기

            for (int q = 0; q < M; q++) { // 각 명령에 대해 반복
                int i, j, k;
                cin >> i >> j >> k; // 명령 입력 받기
                int command_count = j - i + 1; // i부터 j까지의 병사 수 계산

                if (command_count > 1) { // 병사가 1명보다 많아야 합니다.
                    if (k >= i && k <= j) {
                        cout << "NO" << endl; // k번 병사는 명령을 따르지 않음
                    } else {
                        cout << "YES" << endl; // k번 병사가 아닌 다른 병사들이 명령을 따른다.
                    }
                } else {
                    cout << "NO" << endl; // 명령을 따르는 병사가 1명 이하일 경우
                }
            }

            return 0;
        }
    

4단계: 코드 설명

코드의 각 부분에 대해 간단하게 설명하겠습니다.

  • #include <iostream>: C++의 입출력 스트림 라이브러리입니다.
  • using namespace std;: std 네임스페이스를 사용하여 코드 가독성을 높입니다.
  • int N, M;: 각각 병사 수와 명령 수를 저장하는 정수형 변수입니다.
  • cin >> N >> M;: 병사 수와 명령 수를 입력받습니다.
  • for (int q = 0; q < M; q++): M번 반복하여 각 명령에 대해 처리합니다.

    • cin >> i >> j >> k;: 각 명령의 i, j, k 값을 입력받습니다.
    • int command_count = j - i + 1;: 명령을 따르는 병사의 수를 계산합니다.
    • if (command_count > 1): 따르는 병사의 수가 1명보다 많아야 명령이 성립합니다.

      • if (k >= i && k <= j): k번 병사가 범위에 포함되면 “NO”를 출력합니다.
      • cout << "YES" << endl;: k번 병사가 범위 밖에 있을 경우 “YES”를 출력합니다.
    • 각 명령의 출력은 endl을 통해 다음 줄로 이동합니다.

5단계: 테스트 케이스

프로그램의 정확성을 검증하기 위해 몇 가지 테스트 케이스를 고려해야 합니다. 다음은 입력과 출력의 예시입니다.

        입력:
        5 3
        1 3 2
        1 5 5
        2 4 3

        출력:
        NO
        YES
        YES
    

결론

이번 C++ 코딩테스트 강좌에서는 주몽의 명령 문제를 통해 알고리즘 문제 해결 과정과 C++
프로그래밍 기술을 배웠습니다. 문제의 핵심은 주어진 명령을 정확히 이해하고, 이를 효율적으로
처리하는 알고리즘을 구현하는 것이었습니다.
주어진 문제를 해결하는 과정에서 다양한 경우의 수를 고려하는 것이 중요한 점임을 다시 한 번 강조합니다.
이러한 문제를 푸는 과정은 종종 우리가 실제 문제를 해결하는 데 많은 도움을 줄 수 있습니다.

다음 강좌에서는 더 복잡한 알고리즘 문제를 다루어 보도록 하겠습니다. 감사합니다!

C++ 코딩테스트 강좌, 조약돌 꺼내기

1. 문제 정의

주어진 n개의 조약돌 중에서 m개의 조약돌을 꺼내려고 할 때, 어떤 조약돌을 꺼내는 것이 가장 좋은 선택인지 알아보는 문제입니다.
이 문제는 탐욕 알고리즘(Greedy Algorithm)을 사용하여 해결할 수 있습니다. 입력으로는 각 조약돌의 무게가 주어지며,
꺼낼 조약돌들의 총 무게가 최대 K가 되도록 해야 합니다.

2. 문제 이해

입력:
– 첫 번째 줄 : 조약돌의 개수 n (1 ≤ n ≤ 100)
– 두 번째 줄 : 꺼내야 할 조약돌의 개수 m (1 ≤ m ≤ n)
– 세 번째 줄 : 조약돌의 개수 n개에 대한 무게 (1 ≤ weight ≤ 1000)
출력:
– 꺼낸 조약돌들의 무게의 총합이 최대 K가 되도록 할 때, 그 총합을 구하라.

3. 예시

입력 예시:
5
3
1 2 3 4 5
출력 예시:
12 (3 + 4 + 5)

4. 알고리즘 설계

이 문제를 해결하기 위해서는 우선 주어진 조약돌의 무게를 정렬한 후, 가장 무거운 조약돌부터 m개를 선택하는 과정을 거쳐야 합니다.
이때, 선택된 조약돌의 총 무게를 계산하여 결과를 출력합니다. 알고리즘의 단계는 다음과 같습니다.

  • 조약돌의 무게를 오름차순으로 정렬한다.
  • 정렬된 조약돌의 마지막 m개를 선택한다.
  • 선택된 조약돌의 무게를 합산하여 결과를 출력한다.

5. 코드 구현

이제 위의 알고리즘을 바탕으로 C++ 코드를 구현해보겠습니다.


#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    int n, m;
    std::cout << "조약돌의 개수를 입력하세요: ";
    std::cin >> n;
    std::cout << "꺼내야 할 조약돌의 개수를 입력하세요: ";
    std::cin >> m;

    std::vector<int> weights(n);
    std::cout << "조약돌의 무게를 입력하세요 (띄어쓰기로 구분): ";
    for (int i = 0; i < n; ++i) {
        std::cin >> weights[i];
    }

    // 조약돌 무게 정렬
    std::sort(weights.begin(), weights.end());

    // 가장 무거운 m개 조약돌 선택
    int total_weight = 0;
    for (int i = n - m; i < n; ++i) {
        total_weight += weights[i];
    }

    std::cout << "선택된 조약돌의 총 무게: " << total_weight << std::endl;

    return 0;
}

6. 코드 설명

위의 C++ 코드는 주어진 조약돌의 개수와 선택해야 하는 조약돌의 개수를 입력받고, 각 조약돌의 무게를 배열에 저장합니다.
그런 다음, STL의 sort 기능을 사용하여 무게를 정렬합니다. 정렬된 배열에서 가장 무거운 m개를 선택하여 총 무게를 계산하고 출력합니다.

7. 알고리즘 복잡도 분석

이 알고리즘의 시간 복잡도는 조약돌의 개수를 n이라고 할 때, O(n log n)입니다. 정렬 과정이 가장 큰 비중을 차지합니다.
공간 복잡도는 O(n)이며, 이는 입력으로 받은 조약돌의 무게를 저장하기 위해 필요한 메모리입니다.

8. 문제 해결 후기

이 문제는 탐욕 알고리즘의 기초를 배우기에 적합한 연습 문제입니다. 조약돌을 꺼내는 문제를 통해
최적화된 선택을 할 수 있는 방법을 익힐 수 있습니다. 실제 코딩 테스트에서도 유사한 형태의 문제가 자주 출제되므로
반복적인 연습이 필요합니다. 앞으로 다양한 문제를 풀어보면서 더 많은 경험을 쌓아가시길 바랍니다.

C++ 코딩테스트 강좌, 제곱이 아닌 수 찾기

안녕하세요, 여러분! 오늘은 C++을 사용한 코딩테스트 강좌를 통해 “제곱이 아닌 수 찾기”라는 주제로 깊이 있는 문제 풀이를 해보겠습니다. 이 문제는 알고리즘의 기초 개념을 이해하는 데 매우 유용하며, 실제 코딩 면접에서 자주 등장할 수 있습니다.

문제 정의

주어진 정수 배열이 있습니다. 이 배열에서 특정한 범위의 정수를 추출할 때, 해당 범위 내에서 제곱수(2의 제곱, 3의 제곱 등)의 개수가 몇 개인지를 세는 문제입니다. 정확히 말하자면, 배열에서 제곱수가 아닌 수를 찾는 것이죠.

문제 설명

입력:
- 정수 n: 배열의 크기
- 정수 배열 A[n]: n개의 정수로 이루어진 배열
- 정수 m: 범위의 시작 (inclusive)
- 정수 p: 범위의 끝 (inclusive)

출력:
- 제곱수가 아닌 수의 개수

예시:
입력
n = 5
A = [1, 2, 3, 4, 5]
m = 1
p = 5

출력
3  // 2와 3은 제곱수가 아니며, 1과 4는 제곱수입니다.

문제 해결 전략

문제를 해결하기 위해 다음의 절차를 따르겠습니다:

  1. 배열 내의 모든 수를 확인하고 제곱수인지 확인하는 함수를 만듭니다.
  2. 주어진 범위 [m, p] 내의 수들 중에서 제곱수가 아닌 수를 셉니다.
  3. 결과를 출력합니다.

제곱수 판별 함수

제곱수를 판별하기 위해, 각 수의 제곱근을 구하고 이를 정수로 변환한 후, 다시 제곱하여 본래의 수와 같은지 확인하면 됩니다. Python의 경우는 다음의 코드를 사용할 수 있습니다:

bool isPerfectSquare(int x) {
    int s = sqrt(x);
    return (s * s == x);
}

C++에서는 cmath 라이브러리를 사용하여 sqrt() 함수를 사용할 수 있습니다. 제곱수가 아닌 수를 세기 위해, for 루프를 사용하여 해당 범위의 수를 검사하면 됩니다.

C++ 코드 구현

이제 우리가 논의한 내용을 바탕으로 C++ 코드를 구현해보겠습니다.

#include 
#include 
#include 
using namespace std;

bool isPerfectSquare(int x) {
    if (x < 0) return false; // 음수는 제곱수가 아닙니다.
    int s = sqrt(x);
    return (s * s == x);
}

int countNonPerfectSquares(const vector& arr, int m, int p) {
    int count = 0;
    for (int num : arr) {
        if (num >= m && num <= p && !isPerfectSquare(num)) {
            count++;
        }
    }
    return count;
}

int main() {
    int n, m, p;
    cout << "배열의 크기(n)를 입력하세요: ";
    cin >> n;
    vector arr(n);
    
    cout << "배열의 요소를 입력하세요: ";
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }

    cout << "범위의 시작(m)을 입력하세요: ";
    cin >> m;
    cout << "범위의 끝(p)을 입력하세요: ";
    cin >> p;

    int result = countNonPerfectSquares(arr, m, p);
    cout << "제곱이 아닌 수의 개수: " << result << endl;

    return 0;
}

코드 설명

위의 C++ 코드는 다음과 같이 작동합니다:

  1. 사용자로부터 배열의 크기와 배열의 요소를 입력받습니다.
  2. 범위의 시작과 끝을 입력받습니다.
  3. countNonPerfectSquares() 함수를 호출하여 주어진 범위 내에서 제곱수가 아닌 수의 개수를 계산합니다.
  4. 결과를 출력합니다.

테스트 케이스

이제 몇 가지 테스트 케이스를 실행해서 코드가 잘 작동하는지 확인해봅시다.

예시 1:
입력:
5
1 2 3 4 5
1
5

출력:
3 // [2, 3, 5]가 제곱수가 아닙니다.

예시 2:
입력:
6
-1 0 1 2 3 4
0
4

출력:
3 // [0, 2, 3]가 제곱수가 아닙니다.

결론

오늘은 C++ 코딩테스트 강좌를 통해 “제곱이 아닌 수 찾기”라는 주제를 다뤘습니다. 이 문제는 제곱수와 비제곱수의 개념을 이해하고, 배열 내에서 특정 범위의 수를 평가하는 로직을 구현해볼 수 있는 좋은 기회였습니다. 여러분이 이 코드를 통해 알고리즘 문제를 해결하는 방법을 익히고, 실제 코딩 면접에도 대비할 수 있기를 바랍니다.

이 글이 여러분에게 도움이 되었기를 바랍니다. 다음 시간에도 더욱 재미있고 유익한 주제로 찾아뵙겠습니다!

© 2023 Your Blog Name. All rights reserved.