C++ 코딩테스트 강좌, 줄 세우기

문제 설명

N명의 학생들이 교실에서 줄을 서고 있습니다. 각 학생의 키는 배열로 주어지며, 주어진 키 순서와 다르게 서 있을 경우, 학생들은 자신의 위치를 바꿔줄 수 있습니다.
이때, 학생들을 키의 오름차순으로 정렬하기 위한 최소의 스왑 수를 구하는 문제입니다.

입력 형식

첫 번째 줄에 학생의 수 N이 주어집니다. (1 ≤ N ≤ 1000)
두 번째 줄에는 N명의 학생의 키가 공백으로 구분되어 주어집니다. 각 키는 1 이상 1000 이하의 정수입니다.

출력 형식

최소 스왑 수를 출력합니다.

예제 입력/출력

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

문제 풀이

이 문제를 해결하기 위해 우리는 다음 단계를 따릅니다:

1단계: 문제 이해하기

주어진 학생의 키를 오름차순으로 정렬하기 위한 최소 스왑 수를 찾는 것입니다.
예를 들어, 위의 입력에서 학생들은 {5, 3, 2, 4, 1}의 순서로 서 있었고, 이를 {1, 2, 3, 4, 5}로 바꿔야 합니다.
이 과정에서 몇 번의 교환이 필요한지를 세는 것이 목표입니다.

2단계: 접근 방법

최적의 스왑 수를 찾기 위해서는 각 스왑을 통해 배열의 순서를 한 단계씩 정렬하는 것이 효과적입니다.
우리는 다음과 같은 방법으로 이 문제를 해결할 수 있습니다:

  1. 학생들의 키와 그 인덱스를 쌍으로 묶어 주어진 리스트를 정렬합니다.
  2. 각 학생의 현재 위치와 정렬된 위치를 비교합니다.
  3. 스왑을 통해 학생들을 정렬된 위치로 이동해야 하며, 이미 방문한 학생들은 다시 고려하지 않습니다.

3단계: 알고리즘 구현

        #include 
        #include 
        #include 
        
        using namespace std;

        // 구조체 정의: 인덱스와 키를 쌍으로 관리
        struct Student {
            int index;
            int height;
        };

        // 스왑 함수를 정의
        void swap(Student& a, Student& b) {
            Student temp = a;
            a = b;
            b = temp;
        }

        int minSwaps(vector& heights) {
            int n = heights.size();
            vector students(n);
            for (int i = 0; i < n; i++) {
                students[i] = {i, heights[i]};
            }
            
            // 학생들을 키 기준으로 정렬
            sort(students.begin(), students.end(), [](Student a, Student b) {
                return a.height < b.height;
            });
            
            vector visited(n, false);
            int swapCount = 0;
            
            for (int i = 0; i < n; i++) {
                if (visited[i] || students[i].index == i) {
                    continue;
                }
                
                // 사이클의 크기를 계산
                int cycle_size = 0;
                int j = i;
                while (!visited[j]) {
                    visited[j] = true;
                    j = students[j].index;
                    cycle_size++;
                }
                
                if (cycle_size > 0) {
                    swapCount += (cycle_size - 1);
                }
            }
            return swapCount;
        }

        int main() {
            int n;
            cout << "학생 수를 입력하세요: ";
            cin >> n;
            vector heights(n);

            cout << "학생들의 키를 입력하세요: ";
            for (int i = 0; i < n; i++) {
                cin >> heights[i];
            }

            int result = minSwaps(heights);
            cout << "최소 스왑 수: " << result << endl;
            return 0;
        }
    

4단계: 코드 설명

위의 코드는 C++로 작성된 줄 세우기 문제의 해결 방법을 보여줍니다.

  • 구조체 Student: 인덱스와 학생의 키를 쌍으로 묶어 관리합니다.
  • swap 함수: 두 학생의 정보를 스왑합니다.
  • minSwaps 함수: 최소 스왑 수를 계산하는 주 함수입니다. 학생 리스트를 정렬하고, 각 방문 여부를 체크하여 사이클을 계산합니다.
  • main 함수: 사용자로부터 입력을 받고 결과를 출력하는 주 함수입니다.

5단계: 테스트 및 검증

코드를 작성한 후 다양한 케이스로 테스트를 수행합니다.
다양한 입력을 통해 예상되는 출력과 일치하는지 확인해야 합니다.
예를 들어, 가장 간단한 테스트 케이스를 추가하여 정확성을 검사합니다:

        입력:
        3
        3 1 2
        
        출력:
        2
    

추가적으로, 큰 입력을 통해 성능을 확인하는 것도 중요합니다.

결론

이 문제를 통해 스왑과 정렬의 개념, 구조체의 활용 및 알고리즘의 복잡성에 대한 이해를 높일 수 있었습니다.
C++ 코딩테스트에서 자주 출제되는 유형이므로, 충분한 연습을 통해 알고리즘적 사고를 키워야 합니다.

참고자료

© 2023 C++ 코딩테스트 정보 블로그

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. 문제 해결 후기

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