ALGORITHM NOTE1

BOJ 4673 - 셀프 넘버

신기한 숫자

#algorithm#boj#silver#brute-force#implementation#math
아카이브로 돌아가기

문제 링크

문제

셀프 넘버는 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부터 10000까지 모든 d(n) 값을 만들어 보고, 한 번이라도 생성된 수를 표시해 두면 끝까지 표시되지 않은 수가 바로 셀프 넘버가 된다.

현재 코드는 각 수의 자리합을 더해 d(n)을 만들고, 유효한 범위 안에 있으면 배열에 체크한다. 마지막에는 체크되지 않은 수만 순서대로 출력하면 된다.

코드

cpp
#include <iostream>
using namespace std;
#define ARRAY_SIZE 10000
 
bool self_Num[ARRAY_SIZE + 1] = { };
 
void d(int n) {
	if (n <= 10000) {
		self_Num[n - 1] = true;
		int sum = n, a = n, b = 0;
 
		while (a > 0) {
			b = a % 10;
			a = a / 10;
			sum += b;
		}
 
		d(sum);
	}
}
 
int main() {
 
	for (int i = 0; i < ARRAY_SIZE; i++) {
		if (!self_Num[i]) {
			d(i + 1);
			cout << i + 1 << '\n';
		}
	}
 
	return 0;
}

복잡도

  • 시간 복잡도: 상한 LL까지 생성자를 계산하므로 자릿수를 DD라 할 때 O(LD)O(LD)이다.
  • 공간 복잡도: 셀프 넘버 여부 배열을 저장하므로 O(L)O(L)이다.

마무리

생성자로 만들어지는 수를 먼저 표시해 두면 셀프 넘버는 표시되지 않은 수로 남는다. 직접 판별하기보다 생성 결과를 지워 나가는 관점이 핵심이다.