分解平衡数:筛法取素数+质因数分解

题目

最近找实习遇到的题,用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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值