【机试备考】Day18-发财数 | 素数筛法 | 分解质因数

题目

BUPT 2018 计算机 ProblemC
一个大于等于2的整数,如果可以分解为8个或8个以上的素数相乘,则称其为发财数,让你输出第n个发财数

输入描述

输入一个整数t,代表有t组数据(t<1000)
输入一个正整数n,(n<=10000)

输出描述

输出第n个发财数。

示例

输入

1
1

输出

256

题解

王道机试指南上有关于素数筛法分解素因数的相关讲解,当年看过那部分的人肯定就有思路,没看过的基本凉凉

#include<bits/stdc++.h>
#define MAX 350001//最小遍历到35w,就能找到第1w多个发财数
using namespace std;
int isCom[MAX]= {
    
    0}; //合数标记,为1代表是合数
vector<int>prime;//记录素数集合
/*分解质因数,找发财数*/
int cal(int n)
{
    
    
    int count=0;//发财数个数
    for(int i=2; i<MAX; i++)
    {
    
    
        if(isCom[i]==1)//合数
        {
    
    
            int pcount=0;//素因子个数
            int j=0;
            int tmp=i;
            int bound=sqrt(i);//sqrt函数相当耗时,放在while里会超时
            while(j<prime.size()&&prime[j]<bound)
            {
    
    
                if(tmp%prime[j]==0)
                {
    
    
                    tmp/=prime[j];
                    pcount++;//i增加一个素因子
                    if(pcount==8)
                    {
    
    
                        if(++count==n)
                            return i;
                    }
                }
                else
                    j++;
            }
            //最后剩下的是素数且是发财数
            if(tmp!=1&&++pcount==8)
            {
    
    
                if(++count==n)
                    return i;
            }
        }
    }
    return -1;
}
/*欧拉线性筛法*/
void find_prime()
{
    
    
    for(int i=2; i<MAX; i++)
    {
    
    
        //是素数,加入素数集合
        if(isCom[i]==0)
            prime.push_back(i);
        //把"i*最小素数"标记为合数
        for(int j=0; i*prime[j]<MAX&&j<prime.size(); j++)
        {
    
    
            isCom[i*prime[j]]=1;
            //找到最小素因子退出
            if(i%prime[j]==0)
                break;
        }
    }
}
int main()
{
    
    
    find_prime();
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n;
        cin>>n;

        //找第n个发财数
        cout<<cal(n)<<endl;
    }
}

当时有一个临界条件写错了,后来又代码超时,折磨了我好几天…

小结

素数筛法

主要分为这三类
在这里插入图片描述

暴力枚举

/*暴力枚举素数*/
void find_prime()
{
    
    
    for(int i=2;i<MAX;i++)
    {
    
    
        int bound=sqrt(i);
        int flag=1;//素数标志
        for(int j=2;j<=bound;j++)
        {
    
    
            //有可被整除的数则i为合数
            if(i%j==0)
            {
    
    
                flag=0;
                break;
            }
        }
        if(flag)
            cout<<i<<"是素数"<<endl;
        else
            cout<<i<<"是合数"<<endl;
    }
}

普通筛法

/*普通素数筛法*/
void find_prime()
{
    
    
    for(int i=2; i<MAX; i++)
    {
    
    
        //是素数,把倍数标记为合数
        if(isCom[i]==0)
        {
    
    
            prime.push_back(i);//加入素数集合
            for(int k=2; i*k<MAX; k++)
                isCom[i*k]=1;//倍数为合数
        }
    }
}

线性筛法

/*欧拉线性筛法*/
void find_prime()
{
    
    
    for(int i=2; i<MAX; i++)
    {
    
    
        //是素数,加入素数集合
        if(isCom[i]==0)
            prime.push_back(i);
        //把"i*最小素因子之前的素数"标记为合数
        for(int j=0;i*prime[j]<MAX&&j<prime.size();j++)
        {
    
    
            isCom[i*prime[j]]=1;
            //找到最小素因子退出
            if(i%prime[j]==0)
                break;
        }
    }
}

参考视频:基础数论

分解质因数

前提是在素数筛法筛出所有素数之后

/*分解质因数*/
void cal(int n)
{
    
    
    for(int i=2; i<n; i++)
    {
    
    
        if(isCom[i]==1)//合数
        {
    
    
            cout<<i<<"可分解为:";
            int j=0;
            int tmp=i;
            int bound=sqrt(i);//sqrt函数相当耗时,放在while里会超时
            while(j<prime.size()&&prime[j]<=bound)
            {
    
    
                if(tmp%prime[j]==0)
                {
    
    
                    tmp/=prime[j];
                    cout<<prime[j]<<" ";
                }
                else
                    j++;
            }
            //最后剩下的是素数
            if(tmp!=1)
                cout<<tmp;
            cout<<endl;
        }
    }
}

运行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43417265/article/details/113686750