题目
最近找实习遇到的题,用digsum(n)表示一个数字的所有数位的和,比如digsum(105)=1+0+5=6,设n=p0*p1*p2*…*pk,其中pi为素数,可能相同(就是将n进行质因数分解)。如果对于一个合数n来说满足digsum(n)=digsum(p0)+digsum(p1)+…+digsum(pk),那么称n为分解平衡数。现在给定一个m,要求比m大的最小的分解平衡数。
例如4=2*2,digsum(4) = digsum(2)+digsum(2),所以4是一个分解平衡数。
输入输出要求
每组测试数据输入一个整数m,1<=m<=10^8,请输出一个整数表示答案。
样例输入
3
样例输出
4
分析
假设有一个数n,如何判断它是否是质数呢?我们一般用2,3,4…√n 依次作为除数去看n是否可以被整除,为什么只计算到√n呢?因为对于一个数来说,如果有比√n还要大的因数p1,那么一定有一个比√n要小的因数p2满足p1*p2=n,既然n可以被p2整除,那就已经说明n不是质数了,所以最多计算到√n就可以判断是否是质数。
对于m来说,因为m最大为10^8,最多计算到10000就可以知道m是否是质数了。
使用筛法取素数的方法取出10000以内的全部素数,原理就是先设一个数组int isprime[10001],isprime[i]=0表示i是一个素数,isprime[i]=1表示i不是素数,先将isprime全部置为0,从2开始,首先isprime[2]=0说明2是一个质数,然后把10000以内的2的倍数全部置1,即isprime[4]=1,isprime[6]=1,…,接下来判断isprime[3]是否为0,是说明3是一个质数,把3的倍数置1,因为3的两倍其实在对2的倍数置1的时候已经置过了,所以从3*3开始置1,isprime[9]=1,isprime[12]=1,…,接下来由于isprime[4]=1,跳过,isprime[5]=0,所以把从5*5开始的5的倍数置1,isprime[25]=1,isprime[30]=1,…,这样一直处理到10000,然后把isprime[i]=0的i放入到prime数组中,prime数组为2,3,5,7,11…
现在已经有了质数,对于给定整数m,要求比它大的分解平衡数,我们从m+1开始算起,首先判断m+1是否是质数,如果是则算m+2,不是则将m+1进行质因数分解,然后计算数位和,在对m+1进行质因数分解的最后会有一个剩余项,这个剩余项可能是1或者是一个大于10000的质数,如果是后者我们要对这个质数计算数位和加入到求和变量中。
具体实现见代码。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int prime[10011],isprime[10011];
int prm_cnt;
void get_prime()//获取质数存入prime数组中,prm_cnt表示质数个数
{
int n = 10010,now = 2;
prm_cnt = 0;
memset(prime,0,sizeof prime);
memset(isprime,0,sizeof isprime);
while (now <= n)
{
if (!isprime[now])
{
prime[prm_cnt] = now;
prm_cnt++;
for (int i=now*now ; i<=n ; i+=now)
isprime[i] = 1;
}
now++;
}
}
int digsum(int m)//求数位和
{
int sum = 0;
while (m)
{
sum += m%10;
m /= 10;
}
return sum;
}
int main()
{
get_prime();
int m;
cin>>m;
while (1)
{
m++;
//判断是否为质数
if (m < 10010){
if (!isprime[m])
continue;//是质数则continue,回到m++;
}
else {
int flag = 0;
for (int i=0 ; i<prm_cnt ; i++)
if (m%prime[i]==0){
flag = 1;
break;
}
if (!flag)
continue;//是质数则continue,回到m++;
}
int m_sum = digsum(m);//m的数位和
int pri_sum = 0;//分解出的质因数数位和
int now = m;
//质因数分解
for (int i=0 ; i<prm_cnt ; i++)
{
while (now%prime[i]==0){//可以整除就不断自除
pri_sum += digsum(prime[i]);
now /= prime[i];
}
if (now==1) break;
}
if (now != 1) pri_sum += digsum(now);
if (pri_sum == m_sum){
cout<<m<<endl;
break;
}
}
return 0;
}
一个小问题
不记得什么时候写的这篇博客了,最近又遇到求质数的题目,然后发现我的筛法有bug,当n很大的时候i=now*now是会溢出的,所以要在for循环上面加一个判断条件如下:
while (now <= n)
{
if (!isprime[now])
{
prime[prm_cnt] = now;
prm_cnt++;
if (now <= sqrt(n){
for (int i=now*now ; i<=n ; i+=now)
isprime[i] = 1;
}
}
now++;
}
这样就避免了溢出。其实也可以先做标记后存放数据,如下:
for (int i = 2; i <= sqrt(n); i++)
if (!isprime[i]){
for (int j = i*i; j <= n; j+=i)
isprime[j] = 1;
}
for (int i = 2; i <= n; i++)
if (!isprime[i])
prime[prm_cnt++] = i;