【问题描述】 求1~N当中约数个数最多的数,若有多解则输出最小的数。 【输入格式】 输入文件只有一行。这一行有一个数N(1≤N<10^17)。 【输出格式】 输出文件只有一行。这一行有一个数,即所求的数。 【输入样例】 2000 【输出样例】 1680 题目分析: (1)此题最容易想到的是穷举,但是肯定超时。 (2)我们可以知道,计算约数的个数和质因数分解有着很大的联系: 若Q的质因数分解为:Q=p1^k1*p2^k2*…*pm^km(p1…pm为素数,k1…km≥1),则Q有(k1+1)(k2+1)…(km+1)个约数。 但是质因数分解的时间复杂度很高,所以也会超时。 (3)通过以上的公式,我们可以“突发奇想”:为何不能把质因数分解的过程反过来呢? 这个算法就是枚举每一个素数。初始时让m=1,然后从最小的素数2开始枚举,枚举因子中包含0个2、1个2、2个2…k个2,直至m*2^k大于区间的上限N。在这个基础上枚举3、5、7……的情况,算出现在已经得到的m的约数个数,同时与原有的记录进行比较和替换。直至所有的情况都被判定过了。 这个算法的优化:如果p1*p2*p3*……*pk>N(pi表示第i个素数),那么只要枚举到p k-1,既不浪费时间,也不会遗漏。 根据以上的算法,对于上限需要枚举436555171个数,也会超时,不过可以过70-80%的数据。 (4)以上的算法还不是最好的,还可以继续优化。 我们看以下的例子: 6=2*3 10=2*5 6和10的质因数分解“模式”完全相同,所以它们的约数个数是相同的。但是由于3<5,所以6<10。 12=2^2*3 18=3^2*2 12和18的质因数分解“模式”完全相同,所以它们的约数个数是相同的。但是由于12的质因数分解中2的指数大于3的指数,18的质因数分解中3的指数大于2的指数,所以12<18。 根据以上的举例,我们也可以对(3)中的算法进行一个改进:可以在枚举时进行一个优化,使得枚举到的数字中2的指数不小于3的指数,3的指数不小于5的指数……这样我们就能够得到质因数分解“模式”相同的最小数(证明略)。再对于每一个得到的数进行比较和记录。这个算法对于上限只需要枚举34136个数,几乎达到了极限。 参考程序: const a:array[1..15]of integer=(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47); |
#include<stdio.h> #define inf 9999999 int p[20] = {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,51}; long long max,n; long long num; void get(long long m,int f,int t,int pr)//m代表当前值,f当前的素数,t约数的个数,pr指数限制 { if(t>max||(t==max&&m<num)) { num=m; max=t; } int j=0,l=1,nt; long long i=m; while(j<pr) { j++; l++; if(n/i<p[f])break; nt=t*l;//约数的个数 i=i*p[f]; if(i<=n)get(i,f+1,nt,j);//为什么是 j,为了是 前一个素数的指数大于后一个素数的指数 } } int main() { int t; scanf("%d",&t);; while(t--) { max=-1; num=-1; scanf("%lld",&n); get(1,1,1,30); printf("%lld\n",num); } }