追海逐风

导航

pku 1142 Smith Number

/*
Author: JackWang
Date: 01-10-10 20:20
Description: 
求Smith数,Smith数的所有因数的数字之和等于自己的数字之和
例子:

本题中,质数不算Smith数

解题思路:
因为要因式分解,所以要算质数,而且要存储从2开始的一些质数,方便分解计算。
算质数用筛法计算。 
质数要算多少是一个问题,题目保证最大的数是100,000,000以内,在32位int之内。
但是用筛法算100,000,000内的所有质数是耗时很长的,因此只能算一部分。其余部分直接用简单判别法判断。 
简单判别法:判断它是否能整除小于等于它平方根的数,如果能整除,就不是质数,如果都不能整除则它是质数。
因此,计算100,000,000的因数只需要sqrt(10,000,000) = 10,000以内的数即可,因此筛法计算10,000个质数即可。
*/

#include <iostream>
#include <cmath>

using namespace std;

const int N = 1000000;          // 筛法中的最大判断数量
const int P = 78498;            // 在N的范围内质数的数量
const int MAX_PRIME = 999983;   // 在N的范围内最大的质数
char isPrime[N];                // 筛法中是否是质数的布尔向量,0为质数1为合数
int primeNumbers[P];            // 按顺序从小到大的N范围内的所有质数
int primeDigitSum[P];           // 与所有质数相对应的数量和,primeNumbers[i]的数量和 = primeDigitSum[i]

// 计算一个正整数的各个数字之和。
int sumDigits(int n) {
	int sum = 0;
	while (n > 0) {
		sum += n % 10;
		n /= 10;
	}
	return sum;
}

// 初始化:筛法计算质数;并计算这些质数的数字和
void calcprime() {
	// 筛法计算质数
	for (int i = 2; i < N; ++i) {
		if (isPrime[i] == 0) {
			for (int j = i + i; j < N; j += i) {
				isPrime[j] = 1;
			}
		}
	}
	
	int count = 0; // 质数的计数
	for (int i = 2; i < N; ++i) {
		if (isPrime[i] == 0) {
			primeNumbers[count] = i;
			primeDigitSum[count] = sumDigits(i); // 预保存质数的数字和,减少Smith数判断时的计算量
			++count;
		}
	}
}

// 判断一个数是否是质数
bool isPrimeNumber(int n) {
	if (n < 2) {
		return false;
	}
	// 如果小于N,直接用筛法的结果就可以判断。
	if (n < N) {
		return isPrime[n] == 0;
	}
	// 如果大于等于N,则用简单判别法
	int max = (int)sqrt(n) + 1;
	for (int i = 0; i < P && primeNumbers[i] <= max; ++i) {
		if (n % primeNumbers[i] == 0) {
			return false;
		}
	}
	return true;
}

// 判断一个数是否是Smith数,要在N范围内才有效。
bool isSmithNumber(int n) {
	if (isPrimeNumber(n)) {
		return false;
	}
	
	int nSum = sumDigits(n);	
	int pSum = 0;
	
	// 分解因子,分解过程中直接计算数字和
	int i;
	for (i = 0; i < P && primeNumbers[i] <= n; ++i) {
		// 这个循环里计算小于等于MAX_PRIME的因子
		while (n % primeNumbers[i] == 0) {
			n /= primeNumbers[i];
			pSum += primeDigitSum[i];
		}
	}
	// 若此时n>1,则表明n不是MAX_PRIME以内因子产生的合数,
	// 又因为MAX_PRIME以内质数相乘得到的合数一定在10,000,000范围内,
	// 所以n一定是质数,要计算这个因子的数字和
	if (n > 1) {
		pSum += sumDigits(n);
	}
	
	return pSum == nSum;
}

int main() {
	calcprime();

	int n;
	while (cin >> n, n > 0) {
		while (!isSmithNumber(++n));
		cout << n << endl;
	}
	return 0;
}

posted on 2010-10-01 20:47  追海逐风  阅读(278)  评论(0编辑  收藏  举报