|
【问题描述】 求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);
}
}
来源:https://www.cnblogs.com/acSzz/archive/2012/07/24/2606079.html