Data Structure - Algorithm/Baekjoon

[백준 #4673] 셀프 넘버

Alex Han 2025. 3. 4. 04:26
반응형

https://www.acmicpc.net/problem/4673

 

문제

셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다.

양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다. 

예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.
33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다. 

생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다.
1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97

10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

 

입력

입력은 없다.

 

출력

10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.

 

예제 출력

1
3
5
7
9
20
31
42
53
64
 |
 |       <-- a lot more numbers
 |
9903
9914
9925
9927
9938
9949
9960
9971
9982
9993

 

해결 코드

import java.util.Arrays;

public class Main {
	//1부터 10000사이 셀프 넘버 찾기
	
	//시도1 : 셀프 넘버가 아닌 애들을 찾고 제외한 수들을 최종 출력
	//이미 셀프넘버가 아님이 증명된 수는 중간에서 멈추기
	
	static boolean isSelfNumber[] = new boolean[10001]; // 1부터 10000까지 만약 d() 함수에서 결과가 나오면 false로 저장
	
	public static void d(int i, boolean start) {
		if(i > 10000) return;
		if(isSelfNumber[i] == false) return;
		
		if(start == false) isSelfNumber[i] = false; // 첫 시작 숫자를 제외하고 반영.
		
		int sum = i;
		while(i > 0) {
			sum += i % 10;
			i /= 10;
		}
		
		d(sum, false);
		
	}
	
	public static void main(String args[]) {
		
		Arrays.fill(isSelfNumber, true);
		
		for(int i=1; i<=10000; i++) {
			d(i, true);
		}
		
		
		isSelfNumber[1] = true; 
		StringBuffer sb = new StringBuffer();
		for(int i=1; i<=10000; i++) {
			if(isSelfNumber[i] == true) sb.append(i).append("\n");
		}
		
		System.out.println(sb);
	}

}

 

이 문제는 완전탐색(Brute Force) 방식으로 셀프 넘버가 아닌 수를 찾고 결과적으로 제외된 수들을 셀프 넘버로 간주하여 출력하도록 하는 구현문제이다.

 

 

 


로직 분석 (완전탐색 + 재귀 탐색)

 

1. 완전탐색의 개념

완전탐색(Brute Force): 가능한 모든 경우를 직접 확인하여 정답을 찾는 방식

이 코드에서의 완전탐색 요소

1부터 10,000까지 모든 숫자에 대해 d(n)을 구함

d(n)을 계속 호출하면서 생성자가 존재하는 모든 수를 제거

 

2. 전체적인 흐름

1. isSelfNumber 배열을 true로 초기화

2. 1부터 10,000까지 모든 수에 대해 완전탐색 진행

3. 각 숫자의 생성자 수열을 계산(d(n))하여 셀프 넘버가 아닌 수를 false로 설정

4. 남아 있는 true인 숫자(셀프 넘버)를 출력

 

 

 


자료구조 분석

 

boolean isSelfNumber[10001] (크기 O(n))

완전탐색 시 필요한 메모리O(n)으로 유지

true: 셀프 넘버 후보

false: d(n)의 결과로 등장한 숫자(셀프 넘버 X)

 

StringBuffer (출력 최적화)

여러 개의 숫자를 System.out.println 대신 한 번에 출력하여 성능 향상

 

 

 


알고리즘 분석 (완전탐색 + 재귀)

 

1. d(n) 함수 (생성자 수열 계산)

public static void d(int i, boolean start) {
    if (i > 10000) return;
    if (!isSelfNumber[i]) return; // 이미 생성자로 등장한 경우 종료

    if (!start) isSelfNumber[i] = false; // 첫 시작 숫자가 아닌 경우 false 처리

    int sum = i;
    while (i > 0) {
        sum += i % 10;
        i /= 10;
    }

    d(sum, false);
}

 

n에서 각 자리 숫자를 분해하여 d(n)을 계산 (자리수 합)

결과 sum을 이용해 재귀 호출 (d(sum))

sum10000을 넘어서면 종료

 


 

2. 메인 함수 (완전탐색 수행)

public static void main(String args[]) {
    Arrays.fill(isSelfNumber, true); // 셀프 넘버가 아닌 수는 추후에 모두 false로 갱신됨

    for (int i = 1; i <= 10000; i++) {
        d(i, true); // 완전탐색 수행
    }

    StringBuffer sb = new StringBuffer();
    for (int i = 1; i <= 10000; i++) {
        if (isSelfNumber[i]) sb.append(i).append("\n");
    }

    System.out.println(sb);
}

 

1부터 10,000까지 모든 숫자에 대해 d(n)을 수행하는 완전탐색

d(n)을 호출하면서 isSelfNumber 배열을 갱신

 

 

 


시간 복잡도 분석

 

1. 각 숫자 n에 대해 d(n) 계산 (자리수 합 연산)

O(log n)

n=9999일 때 최대 4자리 (9+9+9+9 계산)

4번의 연산 수행 (O(4) ≈ O(1))

 

2. 1부터 10,000까지 모든 수에 대해 d(n) 계산

10,000 * O(log n) ≈ O(n log n)

 

3. 최적화 가능성

중복 호출을 줄이면 O(n)까지 최적화 가능

반복문으로 변경하면 스택 오버플로우 방지

 

 

 


개선된 코드 (반복문 사용)

import java.util.Arrays;

public class Baekjoon4673 {
    static boolean isSelfNumber[] = new boolean[10001];

    public static int d(int n) {
        int sum = n;
        while (n > 0) {
            sum += n % 10;
            n /= 10;
        }
        return sum;
    }

    public static void main(String[] args) {
        Arrays.fill(isSelfNumber, true);

        for (int i = 1; i <= 10000; i++) {
            int num = d(i);
            while (num <= 10000 && isSelfNumber[num]) {
                isSelfNumber[num] = false;
                num = d(num);
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 10000; i++) {
            if (isSelfNumber[i]) sb.append(i).append("\n");
        }
        System.out.println(sb);
    }
}

 

반복문 사용 → 재귀 호출 제거

중복 계산 방지 → while 문을 사용하여 d(n) 반복 수행

성능 향상 → O(n log n) → O(n)로 최적화

반응형

'Data Structure - Algorithm > Baekjoon' 카테고리의 다른 글

[백준 #10845] 큐  (0) 2025.03.24
[백준 #2606] 바이러스  (0) 2025.03.23
[백준 #1158] 요세푸스 문제  (1) 2025.03.02
[백준 #15649] N과 M (1)  (0) 2025.03.02
[백준 #7568] 덩치  (0) 2025.03.02