C++ 코딩테스트 강좌, ATM 인출 시간 계산하기

최근 수년간 IT 업계에서는 알고리즘 및 문제 해결 능력이 중요한 평가 요소로 자리잡고 있습니다. 이러한 기조의 연장선으로, 많은 기업들이 코딩테스트를 통해 지원자의 능력을 평가하고 있습니다.
이번 글에서는 C++을 활용하여 ‘ATM 인출 시간 계산하기’ 문제를 통해 기본적인 알고리즘 문제 해결 능력을 향상시키고자 합니다.

문제 설명

ATM에서는 각 사용자의 인출 요청이 들어올 때마다 요청이 처리되는 데 소요되는 시간이 다릅니다.
예를 들어, 한 사용자가 1분 후에 인출을 요청하면 그 시간은 모두 처리되고, 그 사용자는 1분 후에 인출할 수 있습니다.
만약 여러 사용자가 동시에 인출을 요청할 경우, 요청은 FIFO(선입선출)로 처리되므로, 먼저 요청된 사용자에게 먼저 처리가 이루어집니다.

사용자의 요청이 들어오는 순서와 각 요청의 소요 시간이 주어졌을 때, 모든 사용자가 인출을 완료하기 위해서 필요한 총 시간을 계산하여 출력하는 프로그램을 작성하시오.

입력 형식

  • 첫 번째 줄에는 정수 N(1 ≤ N ≤ 1000)이 주어지며, 이는 인출 요청의 수를 나타냅니다.
  • 두 번째 줄에는 인출 요청에 대한 소요 시간이 공백으로 구분되어 N개의 정수 형태로 주어집니다. (1 ≤ 각 요청 소요 시간 ≤ 100)

출력 형식

  • 모든 사용자가 인출을 완료하는 데 걸리는 총 시간을 출력합니다.

예시 입력

5
3 1 4 3 2

예시 출력

32

문제 해결 과정

문제의 핵심은 각 사용자가 인출을 완료하는 데 필요한 시간을 모두 합산하는 것입니다.
이 과정에서 우리는 요청이 수행되는 순서를 고려해야 합니다. 요청이 FIFO 방식으로 처리되므로, 먼저 요청된 사용자가 먼저 통과합니다.
우리는 이 문제를 해결하기 위해 다음과 같은 단계를 따릅니다.

1단계: 입력값 읽기

먼저 사용자로부터 요청의 수와 요청 시간들을 읽어야 합니다. C++에서는 표준 입력을 통해 데이터를 읽을 수 있습니다.


#include <iostream>
#include <vector>

using namespace std;

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

    vector<int> times(N);
    for (int i = 0; i < N; i++) {
        cin >> times[i];
    }

    return 0;
}

2단계: 총 시간 계산하기

각 사용자의 요청 시간은 요청이 처리되는 동안 그 앞에 있는 요청들 때문에 대기하는 시간이 발생합니다.
따라서 각 사용자가 요청을 완료하기 위해 소요되는 시간은 그들의 요청 소요 시간과 이전 사용자의 요청 소요 시간을 합산해야 합니다.
이를 통해 두 번째 사용자부터는 첫 번째 사용자의 소요 시간만큼의 대기 시간이 발생하게 됩니다.


#include <iostream>
#include <vector>

using namespace std;

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

    vector<int> times(N);
    for (int i = 0; i < N; i++) {
        cin >> times[i];
    }

    int totalTime = 0;
    int currentTime = 0;

    for (int i = 0; i < N; i++) {
        currentTime += times[i]; // 현재까지의 시간에 요청 소요 시간을 더함
        totalTime += currentTime; // 총 시간에 현재까지의 시간을 더함
    }

    cout << totalTime << endl; // 최종 총 시간 출력

    return 0;
}

3단계: 코드 설명

위의 코드에서 첫 번째 부분은 표준 입력으로부터 데이터를 읽기 위한 부분입니다.
vector<int> times(N);는 요청 소요 시간을 저장하기 위한 동적 배열을 생성합니다.
두 번째로, currentTime이라는 변수를 사용하여 현재까지의 시간이 축적됩니다.
사용자의 요청이 들어올 때마다 currentTime에 해당 요청의 소요 시간을 더하고, 이를 총 시간 totalTime에 축적하는 방식입니다.

전체 코드


#include <iostream>
#include <vector>

using namespace std;

int main() {
    int N;
    cin >> N; // 요청 수 입력받기

    vector<int> times(N); // 요청 소요 시간을 담을 벡터 선언
    for (int i = 0; i < N; i++) {
        cin >> times[i]; // 각 요청의 소요 시간 입력받기
    }

    int totalTime = 0; // 모든 요청 소요 시간 누적
    int currentTime = 0; // 현재 처리 시간 누적

    for (int i = 0; i < N; i++) {
        currentTime += times[i]; // 요청 소요 시간을 더함
        totalTime += currentTime; // 현재까지의 총 시간에 추가
    }

    cout << totalTime << endl; // 최종 총시간 출력

    return 0;
}

결론

이와 같은 문제를 해결함으로써, 알고리즘의 이해도와 C++ 프로그래밍 언어에 대한 이해도를 높일 수 있습니다.
각종 코딩테스트나 알고리즘 대회에서 유용하게 사용할 수 있는 기초적인 문제 해결 능력을 기르는 데 도움이 될 것입니다.
다양한 문제를 통해 실력을 쌓고 면접 준비를 하는 것이 중요하며, 이번 강좌를 통해 이러한 기본기를 다질 수 있었으면 합니다.

C++ 코딩테스트 강좌, 2 N 타일 채우기

문제 설명

2*N 타일 채우기 문제는 주어진 2xN 크기의 공간을 1×2 크기의 타일로 채우는 방법의 수를 구하는 문제입니다.
타일은 가로로 또는 세로로 배치할 수 있으며, 전체 2xN 공간을 완전히 채워야 합니다.
이 문제의 핵심은 조합의 수를 계산하는 것이며, 동적 프로그래밍(Dynamic Programming) 기법을 통해 해결할 수 있습니다.

입력 형식

정수 N이 주어집니다. (1 ≤ N ≤ 30)

출력 형식

2xN 공간을 채우는 방법의 수를 정수로 출력합니다.

문제 풀이 과정

2*N 타일 채우기 문제를 해결하기 위해서, 우리는 몇 가지 예제를 통해 문제의 패턴을 찾아야 합니다.
먼저 N이 1일 때와 N이 2일 때의 경우를 생각해봅시다.

예제

  • N = 1:
    • 1가지 방법: 세로로 1개 타일 배치
  • N = 2:
    • 2가지 방법: 가로로 2개 타일을 배치하거나, 세로로 1개 타일 2개 배치

여기서 우리는 다음과 같은 패턴을 발견할 수 있습니다.
N이 커짐에 따라 각 경우를 다음과 같이 나누어 볼 수 있습니다.

– N번째 칸에 타일을 가로로 넣으면, 이전 N-1개의 칸이 남습니다.
– N번째 칸에 타일을 세로로 넣으면, 이전 N-2개의 칸이 남습니다.

따라서, 점화식은 다음과 같이 표현할 수 있습니다:

                dp[n] = dp[n-1] + dp[n-2]
            

이 점화식을 설정했을 때, 초기값을 설정해야 합니다.
dp[1] = 1 (1×2 타일로 세로 배치), dp[2] = 2 (가로 2개 또는 세로 1개 + 세로 1개) 입니다.

이제 이 점화식을 바탕으로 N을 입력받아 방법의 수를 계산하는 C++ 프로그램을 작성해 보겠습니다.

C++ 코드 구현

                
                #include 
                using namespace std;

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

                    int dp[31] = {0};
                    dp[1] = 1;
                    dp[2] = 2;

                    for (int i = 3; i <= N; ++i) {
                        dp[i] = dp[i - 1] + dp[i - 2];
                    }

                    cout << dp[N] << endl;
                    return 0;
                }
                
            

위 코드는 입력받은 N에 대해 dp 배열을 생성하고, 동적 프로그래밍 기법을 사용하여 각 경우의 수를 계산합니다.
마지막에 dp[N]을 출력하여 2xN 타일 채우기 방법의 수를 구합니다.

시간 복잡도

이 알고리즘의 시간 복잡도는 O(N)입니다.
이는 for 루프를 N번 반복하기 때문입니다.
공간 복잡도 또한 O(N)으로 dp 배열을 사용하기 때문입니다.

결론

2*N 타일 채우기 문제는 동적 프로그래밍의 전형적인 예입니다.
문제를 작게 나누어 재귀적으로 해결하는 점화식을 만들고, 이를 통해 문제를 해결하는 것이 핵심입니다.
코딩 테스트에서도 자주 출제되는 문제이니만큼, 이해하고 반복 연습하여 익혀두는 것이 중요합니다.

C++ 코딩테스트 강좌, 022 수 정렬하기 3

문제 설명:
“수 정렬하기 3” 문제는 주어진 숫자들을 정렬하는 문제로, 입력하는 수의 범위가 제한되어 있습니다. 특히, 이 문제의 입력 범위는 1부터 10000까지의 정수이며, 각 정수의 개수는 매우 많을 수 있습니다. 따라서, 우리는 단순한 정렬 알고리즘을 사용하는 대신, 보다 효율적인 방법을 모색해야 합니다. 이 문제는 카운팅 정렬(Counting Sort) 알고리즘을 활용하여 해결할 수 있습니다.

문제의 조건

  • 입력 받는 수의 개수: N (1 ≤ N ≤ 100000)
  • 수의 범위: 1 ≤ 수 ≤ 10000

알고리즘 설명

카운팅 정렬 알고리즘은 정수 값을 정렬하기 위해 주어진 값의 범위를 기준으로 배열을 만들고, 개수를 계산하여 정렬하는 방식입니다. 그 과정은 다음과 같습니다:

  1. 입력할 수의 최대값을 기준으로 배열을 생성한다. 이 경우, 최대값은 10000이다.
  2. 입력할 수의 개수 N만큼 반복하면서, 각 수의 개수를 카운팅 배열에 저장한다.
  3. 카운팅 배열을 이용하여 각 수를 그 수의 개수만큼 출력한다.

이 방법은 O(N + K)의 시간 복잡도를 가지며, K는 수의 범위 (여기서는 10000)입니다. 따라서 입력 데이터가 많더라도 효율적으로 처리할 수 있습니다.

문제 풀이 과정

  1. 입력을 받기 위해 필요한 헤더 파일을 포함한다.
  2. 정렬할 숫자의 개수 N을 입력받는다.
  3. 수의 개수를 저장할 카운팅 배열을 선언하고, 0으로 초기화한다.
  4. N개의 수를 입력받으면서, 해당 숫자의 인덱스에 1을 추가하여 개수를 세는 반복문을 만든다.
  5. 개수가 카운팅된 배열을 참조하여, 각 숫자를 그 수의 개수만큼 출력하는 반복문을 만든다.

C++ 코드


#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n;
    cin >> n; // 숫자의 개수 입력받기
    vector count(10001, 0); // 1부터 10000까지의 수의 개수를 저장할 배열

    // 수 입력받아서 카운팅
    for (int i = 0; i < n; i++) {
        int num;
        cin >> num;
        count[num]++;
    }

    // 카운팅된 배열을 기반으로 정렬된 수 출력
    for (int i = 1; i <= 10000; i++) {
        while (count[i] > 0) {
            cout << i << '\n';
            count[i]--;
        }
    }

    return 0;
}

위 코드 설명

1. 입력: 사용자로부터 정렬할 수의 개수 N과 N개의 정수를 입력받습니다.

2. 카운팅 배열 생성: 카운팅 정렬을 위해 10001 크기의 벡터를 생성합니다. 이 배열의 인덱스는 입력받은 수를 의미하며, 각 인덱스의 값은 해당 숫자의 개수를 저장합니다.

3. 수 카운팅: N개의 수를 반복하여 카운팅 배열의 해당 인덱스를 증가시킵니다. 이를 통해 각 숫자의 빈도를 세는 것입니다.

4. 출력: 카운팅된 결과를 바탕으로, 각 숫자를 빈도만큼 출력합니다. 이 과정에서 각 숫자의 인덱스가 해당 숫자를 식별하게 됩니다.

효율성 분석

이 알고리즘은 시간 효율성이 뛰어나, 최대 100000개의 수를 가진 데이터셋도 문제없이 처리할 수 있습니다. 메모리 측면에서도 카운팅 배열은 10001의 고정 크기로 메모리를 추가로 소모하지 않으므로, 전반적으로 좋은 성능을 보입니다.

종합 정리

카운팅 정렬을 사용하여 “수 정렬하기 3” 문제를 해결하는 과정은 분명 효율적이며, 특히 입력값의 범위가 제한되어 있는 특성이 이 방법의 유용성을 강조합니다. 이 문제를 통해 카운팅 정렬의 개념을 잘 이해할 수 있으며, C++의 벡터를 활용하여 데이터 구조와 알고리즘을 결합하는 방법에 대해 학습할 수 있습니다.

심화 내용 및 응용

카운팅 정렬은 특정 상황에서 매우 효율적이지만, 모든 정렬 문제에 적합하지 않는다는 점을 명심해야 합니다. 이 알고리즘은 정수 또는 열거형 데이터를 정렬할 때에만 사용할 수 있으며, 실수와 같이 범위가 넓은 데이터를 다룰 때는 다른 알고리즘(예: 퀵 정렬, 병합 정렬 등)을 고려하는 것이 좋습니다.

또한, 카운팅 정렬은 수의 범위가 작을 때에 가장 큰 장점을 가지므로, 이러한 특성을 가진 문제에서는 항상 고려해야 할 정렬 알고리즘 중 하나입니다.

결론: “수 정렬하기 3” 문제는 카운팅 정렬을 통해 손쉽게 해결할 수 있는 예시입니다. 이 강좌를 통해 C++ 코딩 능력을 한층 더 발전시킬 수 있기를 바랍니다!