문제 설명
주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return 하도록 solution 함수를 완성해주세요.

제한사항
nums에 들어있는 숫자의 개수는 3개 이상 50개 이하입니다.
nums의 각 원소는 1 이상 1,000 이하의 자연수이며, 중복된 숫자가 들어있지 않습니다.


입출력 예
nums result
[1,2,3,4] 1
[1,2,7,6,4] 4


입출력 예 설명
입출력 예 #1
[1,2,4]를 이용해서 7을 만들 수 있습니다.

입출력 예 #2
[1,2,4]를 이용해서 7을 만들 수 있습니다.
[1,4,6]을 이용해서 11을 만들 수 있습니다.
[2,4,7]을 이용해서 13을 만들 수 있습니다.
[4,6,7]을 이용해서 17을 만들 수 있습니다.


설계

입력값의 조합을 만들고 그 조합의 합이 소수라면 Count를 1씩올려 return 하게 만든다.

 

구현

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

bool PrimeNum(int num)
{
    int count = 0;

    for (int i = 1; i <= num; i++)
    {
        if (num % i == 0)
        {
            count++;
        }
    }

    if (count == 2)
    {
        return true;
    }

    return false;
}


int solution(vector<int> nums) {
    int answer = 0;
    int total = 0;
    if (nums.size() == 3)
    {
        total = nums[0] + nums[1] + nums[2];
        if (PrimeNum(total))
        {
            answer++;
        }
    }
    else
    {
        for (int i = 0; i < nums.size() - 2; i++)
        {
            for (int j = i+1; j < nums.size() - 1; j++)
            {
                for (int k = j+1; k < nums.size(); k++)
                {
                    total = nums[i] + nums[j] + nums[k];

                    if (PrimeNum(total))
                    {
                        answer++;
                    }
                }
            }
        }
    }

    return answer;
}

리펙토링

#include <vector>
#include <iostream>

using namespace std;

// 숫자가 소수인지 판별하는 함수 (최적화 버전)
// true를 반환하면 소수, false를 반환하면 소수가 아님
bool isPrime(int num) {
    // 1. 예외 처리: 1 이하의 숫자는 소수가 아닙니다.
    // (이 문제에서는 3개의 합이 최소 6 이상이므로 굳이 필요 없지만, 
    // 소수 판별 함수의 완전성을 위해 넣어두는 것이 좋습니다.)
    if (num <= 1) return false;

    // 2. 2부터 num의 제곱근(i * i <= num)까지만 검사합니다.
    // 굳이 모든 숫자를 다 나눠볼 필요 없이, 나누어 떨어지는 수가 하나라도 있다면
    // 즉시 소수가 아님(false)을 반환하고 함수를 종료하여 실행 시간을 아낍니다.
    for (int i = 2; i * i <= num; i++) {
        if (num % i == 0) {
            return false; // 약수가 발견되었으므로 소수가 아님!
        }
    }

    // 3. 위의 반복문을 무사히 통과했다면 약수가 없다는 뜻이므로 소수(true)입니다.
    return true;
}

int solution(vector<int> nums) {
    int answer = 0; // 소수가 되는 경우의 수를 누적할 변수
    int total = 0;  // 3개 숫자의 합을 임시로 저장할 변수

    // nums.size() == 3 인 경우의 예외 처리를 지웠습니다.
    // 아래 3중 for문은 배열의 크기가 3이어도 조건에 맞게 딱 1번만 순회합니다.

    // 1번 숫자 선택 (마지막 두 자리를 남겨둬야 하므로 nums.size() - 2 까지)
    for (int i = 0; i < nums.size() - 2; i++) {
        
        // 2번 숫자 선택 (1번 숫자 다음부터 시작, 마지막 한 자리를 남겨둬야 함)
        for (int j = i + 1; j < nums.size() - 1; j++) {
            
            // 3번 숫자 선택 (2번 숫자 다음부터 배열의 끝까지)
            for (int k = j + 1; k < nums.size(); k++) {
                
                // 선택된 서로 다른 3개의 숫자를 더합니다.
                total = nums[i] + nums[j] + nums[k];

                // 더한 값(total)이 소수인지 판단 함수를 호출하여 확인합니다.
                if (isPrime(total)) {
                    answer++; // 소수라면 경우의 수를 1 증가시킵니다.
                }
            }
        }
    }

    return answer; // 최종 누적된 소수의 개수 반환
}

주요 리팩토링 포인트

1. 소수 판별 함수(Prime Check)의 비약적인 속도 개선

기존의 PrimeNum 함수는 1부터 num까지 모든 숫자로 나누어보며 확인합니다. (시간 복잡도 $O(N)$)

하지만 수학적으로 어떤 수 $N$의 약수들은 항상 $\sqrt{N}$ (제곱근)을 기준으로 짝을 이룹니다.

따라서 1과 자기 자신을 제외하고, 2부터 $\sqrt{N}$까지만 나누어 떨어지는지 검사하면 검사 횟수를 획기적으로 줄일 수 있습니다. (시간 복잡도 $O(\sqrt{N})$)

2. 불필요한 예외 처리(if-else) 제거

nums.size() == 3일 때를 위해 따로 작성하신 코드는, 사실 아래쪽에 있는 3중 for문이 스스로 완벽하게 처리할 수 있는 범위입니다. 배열 크기가 3이면 3중 반복문이 정확히 딱 한 번만 실행되기 때문입니다. 이 부분을 제거하면 코드가 훨씬 간결해집니다.

3. 직관적인 네이밍

함수 이름을 PrimeNum(명사형)에서 isPrime(논리형/동사형)으로 변경하여, 이 함수가 true/false를 반환한다는 것을 이름만 보고도 알 수 있게 했습니다.


회고

문제를 풀며 시간복잡도를 고려해서 구현해 봤는데, 더 단축시킬 수 있는법을 AI를 통해 알 수 있었다. Solution 함수의 입력 vector사이즈가 3개일때와 아닐때를 구별하였는데 그 부분을 for문이 이미 가지고 있는 로직이였고, 소수 판별 함수에서 굳이 모든 수로 나눠 볼것이 아니라 2번이상이 나오면 탈출하는 구문으로 시간을 더 단축할 수 있음을 알 수 있었다.

'코드테스트' 카테고리의 다른 글

[level 1] 기사단원의 무기 - 136798  (0) 2026.03.13
덧칠하기 - 161989  (0) 2026.03.09
[level 1] 모의고사 - 42840  (0) 2026.03.06
과일장  (0) 2026.02.20
카드 뭉치 - 코드카타  (0) 2026.02.13

+ Recent posts