Home [24441] 행운 수 판정
Post
Cancel

[24441] 행운 수 판정

문제 링크 : https://www.acmicpc.net/problem/24441

24441 - 행운 수 판정

본문

행운 수(Lucky numbers)는 에라토스테네스의 체와 비슷한 방법으로 만들어지는 자연수의 부분집합 또는 그 부분집합의 원소를 말한다.

행운 수의 집합은 다음과 같은 과정을 통해 구성할 수 있다.

  1. 홀수 자연수의 목록을 만든다.
  2. 목록에 속한 수 중 $1$보다 크면서 선택한 적이 없는 수 중에서 가장 작은 수를 선택한다.
  3. 2.에서 선택한 자연수를 $k$라고 할 때, 목록에서 오름차순으로 $i \times k$ ($i \geq 1$) 번째에 해당하는 모든 자연수를 지운다.
  4. 2.로 돌아간다.

다음은 위 과정의 일부를 수행하는 예시이다.

  1. $1$ $3$ $5$ $7$ $9$ $11$ $13$ $15$ $17$ $19$ $21$ $23$ $25$ $\cdots$
  2. $1$ $\underline{\textbf{3}}$ $5$ $7$ $9$ $11$ $13$ $15$ $17$ $19$ $21$ $23$ $25$ $\cdots$
  3. $1$ $3$ $\cancel{5}$ $7$ $9$ $\cancel{11}$ $13$ $15$ $\cancel{17}$ $19$ $21$ $\cancel{23}$ $25$ $\cdots$
  4. $1$ $3$ $7$ $9$ $13$ $15$ $19$ $21$ $25$ $\cdots$
  5. $1$ $3$ $\underline{\textbf{7}}$ $9$ $13$ $15$ $19$ $21$ $25$ $\cdots$
  6. $1$ $3$ $7$ $9$ $13$ $15$ $\cancel{19}$ $21$ $25$ $\cdots$
  7. $1$ $3$ $7$ $9$ $13$ $15$ $21$ $25$ $\cdots$
  8. $1$ $3$ $7$ $\underline{\textbf{9}}$ $13$ $15$ $21$ $25$ $\cdots$
  9. $\cdots$

자연수 $n$이 주어지면 $n$이 행운 수인지 아닌지 알아보자.

입력

첫째 줄에 테스트 케이스의 개수 $T$가 주어진다. ($1 \leq T \leq 10\,000 = 10^4$)

각 테스트 케이스는 한 줄로 이루어져 있으며, 자연수 $n$이 주어진다. ($1 \leq n \leq 1\,000\,000 = 10^6$)

출력

각 테스트 케이스마다 $n$이 행운 수이면 “lucky”를, $n$이 행운 수가 아니면 “unlucky”를 큰따옴표 없이 출력한다.

제한

시간 제한메모리 제한
3sec1024MB

풀이

행운 수라는 것들은 이미 정의되어 있다. 입력 $n$이 달라진다고 해서 행운 수가 달라지지는 않는다. 즉 답을 미리 계산해두는 것이 가능하다는 것. 숫자의 범위가 크지 않기 때문에, 쿼리 시작 전에 모든 행운 수를 미리 찾아서 기록해두도록 하자.

행운 수를 찾는 함수로 getLuckyNumber()라는 함수를 만들자. 이 함수는 $1$부터 $1\,000\,000$까지의 행운 수를 모두 찾는 함수이다. 이 함수 자체가 돌아가는 과정이 3초를 넘기 때문에, 결과값은 문자열로 출력하여 다른 프로그램에서 문자열만 읽어도 이전의 결과를 기록할 수 있도록 하자. 그럼 이 함수는 시간에 구애받지 않고 구현해도 되므로, $O(n^2)$ 완전탐색으로 구현해도 크게 문제되지 않는다.

사용 언어 : C

최종 수정일 : 2023 / 6 / 22

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void get_init() {
    for (int i = 1; i < n; ++i) {
        arr[i] = i * 2 - 1;
    }
    return;
}

int getLuckyNumber() {
    get_init();

    for (int i = 2; i < n; ++i) {
        int k = arr[i];
        printf("Lucky Number : %d\n", k);
        if (k == 0) {
            break;
        }

        top = 1;
        for (int j = 1; j < n; ++j) {
            if (j % k != 0) {
                tempArr[top++] = arr[j];
            }
        }

        for (int j = 0; j < top; ++j) {
            arr[j] = tempArr[j];
        }
        for (int j = top; j < n; ++j) {
            arr[j] = 0;
        }
    }

    FILE* f = fopen("input.txt", "w");
    for (int i = 0; i < top; ++i) {
        if (arr[i] > 0) {
            fprintf(f, "%d,", arr[i]);
        }
    }
    fclose(f);
    return 0;
}

이러면 “input.txt” 파일에는 모든 행운 수가 적힌 문자열이 만들어진다. 앞부분만 조금 떼보면 1,3,7,9,13,15,21,25,31,33,37,43,49,51,63,67, 식으로 작성되는 것이다. 이것을 코드에 다시 넣고 숫자들만 뽑아내어 행운 수로 활용하면 된다. 활용하는 방법으로는 아래와 같은 방법을 사용했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void init() {
    int temp = 0;

    for (int i = 0; resultString[i] != '\0'; ++i) {
        switch (resultString[i]) {
            case ',': {
                isLuckyNumber[temp] = true;
                temp = 0;
                break;
            }
            default: {
                temp *= 10;
                temp += (resultString[i] - '0');
            }
        }
    }

    return;
}

그러면 isLuckyNumber[]은 index가 행운 수인지 아닌지로 표기하게 된다. 나머지는 쿼리 $q$에 따라 isLuckyNumber[q]를 출력하면 끝.

  • 참고 알고리즘 :

코드

사용 언어 : C

최종 수정일 : 2023 / 6 / 22

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
void get_init() {
    for (int i = 1; i < n; ++i) {
        arr[i] = i * 2 - 1;
    }
    return;
}

int getLuckyNumber() {
    get_init();

    for (int i = 2; i < n; ++i) {
        int k = arr[i];
        printf("Lucky Number : %d\n", k);
        if (k == 0) {
            break;
        }

        top = 1;
        for (int j = 1; j < n; ++j) {
            if (j % k != 0) {
                tempArr[top++] = arr[j];
            }
        }

        for (int j = 0; j < top; ++j) {
            arr[j] = tempArr[j];
        }
        for (int j = top; j < n; ++j) {
            arr[j] = 0;
        }
    }

    FILE* f = fopen("input.txt", "w");
    for (int i = 0; i < top; ++i) {
        if (arr[i] > 0) {
            fprintf(f, "%d,", arr[i]);
        }
    }
    fclose(f);
    return 0;
}

void init() {
    int temp = 0;

    for (int i = 0; resultString[i] != '\0'; ++i) {
        switch (resultString[i]) {
            case ',': {
                isLuckyNumber[temp] = true;
                temp = 0;
                break;
            }
            default: {
                temp *= 10;
                temp += (resultString[i] - '0');
            }
        }
    }

    return;
}

int main() {
    /*
    getLuckyNumber();
    return 0;
    */

    int t;
    scanf("%d", &t);

    while (t--) {
        int a;
        scanf("%d", &a);
        printf("%s\n", printTemplate[isLuckyNumber[a]]); // "yes" or "no"
    }
    return 0;
}
  • [Hint] $1$부터 $1\,000\,000$까지 행운 수를 계산하였을 때, 그 개수는 71918개라고 한다.
This post is licensed under CC BY 4.0 by the author.