算法题记录--整除问题(上交研究生复试上机题)

算法思路来自 https://blog.csdn.net/a1106089736/article/details/79230981。本文对问题的基本原理进行解释,以及对代码进行了标注,仅用作学习用途。

一、题目描述:

给定n,a求最大的k,使n!可以被a^k整除但不能被a^(k+1)整除。

输入描述:

两个整数,n(2≤n≤1000),a(2≤a≤1000)

输出描述:

一个整数。

示例1:

输入:6 10

输出:1


二、分析:考虑最大的输入n=1000,则 n! 将会是十分大的整数,所以用常规数据类型来存放 n! 然后去整除a的幂次显然是不可行的。

又知道,每一个非素数均可以表示成多个素数的乘积。则n和a,以及a^k将分别可以表示为

算法题记录--整除问题(上交研究生复试上机题)_第1张图片

①若a可以整除n,则a的任何一个素因子均会在n的素因子集合中出现,即 

且q1,q2...qn在a中出现的次数均不超过其在n中出现的次数。以上两个条件必须同时满足,否则a不可以整除n。

有了上述的基础,则可以将整除问题转化为判断两个数的素因子是否满足上述的两个条件。

②接下来,由于是求n的阶乘,所以需要求的是 n! 的素因子及相应的次数。而 n!=1*2*3*....n,包含了从1到n,里面的最大数为n,则可以分析得知 n! 的素因子均是小于 n 的(这样就确定了要求的素数的上界)。

③又,假设n的某个素因子为p,则p的每个倍数(1*p,2*p,...)均为n! 至少提供一个p因子,则总至少提供p因子的个数为 n/p 个;p^2的每个倍数均至少为 n! 提供两个p因子,但是此处与前面p的每个倍数重复了一次,因此总至少提供p因子的个数增加 n/p^2 个。以此类推,则p因子在 n! 中出现的次数可以统计出来。

④对于a的素因子出现的次数,可以用整数分解质因数的算法解决。

⑤既然 a 和 n! 的素因子的出现次数均已统计出来,接下来就需要求最小的整数 k。显然,a 的素因子的次数乘上倍数 k 之后必须不能超过其在 n! 出现的次数,则仅需用其在 n! 中出现的次数除以其在 a 中出现的次数即可得到倍数 k,然后从众多倍数中挑选出最小的一个即可。

按照上述思路:

三、具体代码

#include
#include
#include
#include
using namespace std;
const int maxn=1001;

vector prime;
bool isprime[maxn];
void prime_init(){ //求出小于n的所有素数
	memset(isprime,1,maxn);
	isprime[0]=false; isprime[1]=false; 
	
	for(int i=2;i1){ //当a≠1时,表示此时的a就是素数
			prime_a[count][0]=a;
			prime_a[count][1]++;
			count++;
		}
		//以下遍历求解a能否整除 n!
		int min_times=maxn; //记录最小的倍数k
		bool tag=true; //tag 作为能够整除的标志
		int j, c; //j 用作素因子的幂指数,c 用作记录素因子在 n! 中出现的次数
		for(int i=0;i0){ //需要注意此处的判断条件与上面if的判断条件,原理一致
					int temp=(int)pow(double(prime_a[i][0]),double(j)); //temp 存放素因子的幂次
					c+=n/temp; //不断累加素因子在n! 中出现的次数
					j++;
					if(n/temp==0){
						break; //当素因子的幂次大于n时,不用再继续求了
					}
				}
				if(cc){
						min_times=c; //得出最小的k
					}
				}
			}
		}
		if(tag){
			printf("%d\n",min_times); //输出最小k
		}else{
			printf("-1\n"); //不能整除输出"-1"
		}
	}
	return 0;
}

你可能感兴趣的:(C++,算法)