题目描述
正整数x 的约数是能整除x 的正整数。正整数x 的约数个数记为div(x)。例如,1,2,5,10 都是正整数10 的约数,且div(10)=4。设a 和b 是2 个正整数,a≤b,找出a 和b之间约数个数最多的数x
输入
输入2 个正整数a≤b≤5000000,编程计算a 和b 之间约数个数最多的数。
输出
程序运行结束时,找到a 和b 之间约数个数最多的数是x,将div(x)输出
样例输入
1 36
样例输出
9
Code
n的约数的计算
Code 1 最笨的计算【时间超限】
从1-n进行循环找到约数,但是这种方法并不可取,肯定会时间超限的
#include<stdio.h>
int main()
{
int a,b,max=0,i,j,cnt;
//scanf("%d%d", &a,&b);
a=1; b=50;
for(i=a;i<=b;i++){
for(j=1,cnt=0;j<=i;j++){
if(i%j==0) cnt++;
}
printf("%-2d: %-6d", i,cnt);
if((i-a+1)%8==0) printf("\n");
}
return 0;
}
1~50
1 : 1 2 : 2 3 : 2 4 : 3 5 : 2 6 : 4 7 : 2 8 : 4
9 : 3 10: 4 11: 2 12: 6 13: 2 14: 4 15: 4 16: 5
17: 2 18: 6 19: 2 20: 6 21: 4 22: 4 23: 2 24: 8
25: 3 26: 4 27: 4 28: 6 29: 2 30: 8 31: 2 32: 6
33: 4 34: 4 35: 4 36: 9 37: 2 38: 4 39: 4 40: 8
41: 2 42: 8 43: 2 44: 6 45: 6 46: 4 47: 2 48: 10
49: 3 50: 6
Code 2 开方处理【时间超限】
但是根据下面的规律
1 1
2 1 2
3 1 3
4 1 2 4
5 1 5
6 1 2 3 6
7 1 7
8 1 2 4 8
当1<=i<sqrt(n),此时的个数记为a
然后进行中间的判断:n%sqrt(n)==0? 此时个数记为b
总数为:2*a+b
时间复杂度还是很大,不过先试试,暂时想不到更好的
得出下面的代码
#include<stdio.h>
int main()
{
int a,b,max=0,i,j,cnt;
scanf("%d%d", &a,&b);
//a=1; b=50;
for(i=a;i<=b;i++){
for(j=1,cnt=0;j*j<i;j++){
if(i%j==0) cnt+=2;
}
if(j*j==i) cnt+=1;
if(max<cnt) max=cnt;
}
printf("%d\n", max);
return 0;
}
1~50得到的结果和足本的方法相同
对时间超限的情况进行分析:比如我的电脑,计算从1~500000之间的最多约束用了半分钟,肯定的时间超限啊,但是为什么会这样呢?
因为时间复杂函数是sqrt(1)+sqrt(2)+…+sqrt(n),还是很大的
然后进行找规律
找规律
规律1:每12个数产生一个最大值(猜想)【Wrong】
1~50
1 : 1 2 : 2 3 : 2 4 : 3 5 : 2 6 : 4 7 : 2 8 : 4 max:6: 4和8: 4
9 : 3 10: 4 11: 2 12: 6 13: 2 14: 4 15: 4 16: 5 max:12: 6
17: 2 18: 6 19: 2 20: 6 21: 4 22: 4 23: 2 24: 8 max:24: 8
25: 3 26: 4 27: 4 28: 6 29: 2 30: 8 31: 2 32: 6 max:30: 8
33: 4 34: 4 35: 4 36: 9 37: 2 38: 4 39: 4 40: 8 max:36: 9
41: 2 42: 8 43: 2 44: 6 45: 6 46: 4 47: 2 48: 10 max:48: 10
49: 3 50: 6
找到什么规律:从0开始,每12个就会有一个峰值
换一种排列方式
1 : 1 2 : 2 3 : 2 4 : 3 5 : 2 6 : 4 7 : 2 8 : 4 9 : 3 10: 4 11: 2 12: 6
13: 2 14: 4 15: 4 16: 5 17: 2 18: 6 19: 2 20: 6 21: 4 22: 4 23: 2 24: 8
25: 3 26: 4 27: 4 28: 6 29: 2 30: 8 31: 2 32: 6 33: 4 34: 4 35: 4 36: 9
37: 2 38: 4 39: 4 40: 8 41: 2 42: 8 43: 2 44: 6 45: 6 46: 4 47: 2 48: 10
49: 3 50: 6 51: 4 52: 6 53: 2 54: 8 55: 4 56: 8 57: 4 58: 4 59: 2 60: 12
输入:4000000 5000000
输出:384 4324320
4233600
4999969: 8 4999970: 16 4999971: 4 4999972: 12 4999973: 4 4999974: 16
4999975: 6 4999976: 8 4999977: 12 4999978: 4 4999979: 4 4999980: 48
4999981: 4 4999982: 8 4999983: 8 4999984: 20 4999985: 8 4999986: 24
4999987: 4 4999988: 12 4999989: 24 4999990: 24 4999991: 4 4999992: 16
4999993: 8 4999994: 4 4999995: 128 4999996: 6 4999997: 4 4999998: 16 峰值
4999999: 2 5000000: 56 5000001: 8 5000002: 16 5000003: 8 5000004: 18
5000005: 8 5000006: 32 5000007: 8 5000008: 32 5000009: 12 5000010: 16
5000011: 2 5000012: 6 5000013: 6 5000014: 8 5000015: 4 5000016: 80
规律:每逢12个必会产生一个最大值,如果多个12个数中间出现一个峰值,则确定了这个数就是最大值
但是这种查找峰值的方式过于复杂,不好使用
规律2:使用10以内的素数进行判断【时间超限】
素数: 2 3 5 7
n
1 1%2=1 1
2 2%2=0 2
3 3%2=1 3%3=0 1*2*1*1
4 4%2=0 2%2=0 1*3*1*1
...
统计数可以被n整除的素数的累计除的次数,然后进行相乘
比如36=2*2*3*3
约数个数=(2+1)*(2+1)
#include<stdio.h>
#include<string.h>
#define MAXM 75
int prime[MAXM] = {2,3,5,7};
void Prime(void)
{
int i=1,j=0,k,sign;
for(i=4,j=11;i<MAXM;j++){
for(k=0,sign=1;k<4&&sign;k++){
if(j%prime[k]==0) sign=0;
}
if(sign) prime[i++]=j;
}
}
int Divide(int n,int a[])
{
int i,sign,max=0;
for(i=0,sign=0;i<MAXM&&n&&n>=prime[i];i++){
while(n%prime[i]==0&&n){
a[i]++;
n/=prime[i];
sign=1;
}
max++;
}
return sign?max:0;;
}
int Product(int a[],int max)
{
int i=-1,pro=1;
while(++i<max)
pro*=(a[i]+1);
return pro;
}
int main()
{
int a, b, max, i, cnt, ia[MAXM],sign;
scanf("%d%d", &a, &b);
Prime();
for(i=a,max=0;i<=b;i++){
memset(ia,0,sizeof(ia));
sign=Divide(i,ia);
if(sign) cnt=Product(ia,sign);
else cnt=2;
if(max<cnt){
max=cnt;
}
}
printf("%d\n", max);
return 0;
}
#include<stdio.h>
int main()
{
int a,b,max=0,i,j,cnt;
//scanf("%d%d", &a,&b);
a= 4999969; b = 5000016;
for(i=a;i<=b;i++){
for(j=1,cnt=0;j*j<i;j++){
if(i%j==0) cnt+=2;
}
if(j*j==i) cnt+=1;
printf("%-2d: %-6d", i,cnt);
if((i-a+1)%6==0) printf("\n");
}
return 0;
}
优化
进行寻找公约数的个数的过程中,肯定有一个可以缩减循环次数的方法,但是怎么去找减少循环次数的方法呢?
其他方法
Method 1 填充数组
将多个素数的乘积作为数组下标,存储约数的个数
约数的个数计算方法
素数: 2 3 5 7
n
1 1%2=1 1
2 2%2=0 2
3 3%2=1 3%3=0 1*2*1*1
4 4%2=0 2%2=0 1*3*1*1
...
统计数可以被n整除的素数的累计除的次数,然后进行相乘
比如36=2*2*3*3
约数个数=(2+1)*(2+1)
该博客探讨了如何在给定的范围内找到约数最多的正整数问题。作者分析了时间超限的计算方法,如最笨的计算和开方处理,并尝试找出规律以优化解决方案。提出了每12个数可能产生最大值的猜想,以及使用10以内素数判断的尝试,但都未成功。最后,讨论了填充数组的优化方法作为潜在的解决方案。

1758

被折叠的 条评论
为什么被折叠?



