最大约数问题——只有解析,代码不正确,一直很纠结
2010-11-19 22:33
253 查看
这个最大约数问题一直A不了,虽然一些简单的能够运行成功,但可能机子测试的时候用的数据太大吧,不过在网上找到一些关于这个问题的解析,虽然王晓东老师给的代码实在是没看懂(也没这个耐心看),但下面这段解析还是比较好的,也要花点时间看吧~~
上面的代码不知道是什么语言,但思路和王晓东老师给的代码思路一样,所以下面王晓东老师的代
码实现:
Code:
#include<iostream>
using namespace std;
#define max Max
const long MAXP = 100000;
long prim[MAXP];
long max, numb, PCOUNT; //max存放最多约数个数,numb存放约数个数最多的数
void primes(); //用筛选法产生质数存于prim数组中
long l, u;
void search(long from, long tot, long num, long low, long up);
int main()
{
primes();
cin >> l >> u;
if ((l == 1) && (u == 1))
{
max = 1;
numb = 1;
}
else
{
max = 2;
numb = l;
search(1, 1, 1, l, u);
}
cout << max << endl << numb << endl;
system("pause");
return 0;
}
void primes()
{
bool get[MAXP+1];
long i;
for (i = 2; i <= MAXP; i++)
get = true;
for (i = 2; i <= MAXP; i++)
if (get[i])
{
long j = i + i;
while (j <= MAXP)
{
get[j] = false;
j += i;
}
}
long ii, j;
for (ii = 2, j = 0; ii <= MAXP; ii++)
if (get[ii]) prim[++j] = ii;
PCOUNT = j;
}
// 区间[low,up]上,tot为当前约数最多个数,num为约数个数最多的数,
//from表示现在是第几个质数。
void search(long from, long tot, long num, long low, long up)
{
if (num >= l)
if ( (tot > max) || ((tot == max) && (num < numb)) )
{
max = tot;
numb = num;
}
if ((low == up) && (low > num)) search(from, tot*2, num*low, 1, 1);
for (long i = from; i <=PCOUNT; i++)
{
if (prim[i] > up) return;
else
{
long j = prim[i], x = low - 1, y = up, n = num, t = tot, m = 1;
while (true)
{
m++;
t += tot;
x /= j;
y /= j;
if (x == y) break;
n *= j;
search(i+1, t, n, x+1, y);
}
m = 1 << m;
if (tot < max / m) return;
}
}
}
下面还贴一个图片解释:在网上找的哦,呵呵
但是不会用,我觉得CSDN传图片的功能好奇怪,不太好搞,算了,不传了,给大家一个博客地址,可以去看看~~
http://hi.baidu.com/1314lingxq/blog/item/5b545916e47dda5ef819b8fa.html
如果大家有什么好的想法,一定写在下面哦,这个问题真的很让人纠结~~~
[align=center]Divisors解题报告[/align]
[align=center]苏州中学 戴文渊[/align]
【问题】
数学家们喜欢各种类型的有奇怪特性的数。例如,他们认为945是一个有趣的数,因为它是第一个所有约数之和大于本身的奇数。
为了帮助他们寻找有趣的数,你将写一个程序扫描一定范围内的数,并确定在此范围内约数个数最多的那个数。不幸的是,这个数和给定的范围的都比较较大,用简单的方法寻找可能需要较多的运行时间。所以请确定你的算法能在几秒内完成最大范围内的扫描。
输入格式
[align=left]从文件divisors.in输入。文件只有一行,给出扫描的范围,由下界[i]L和上界U确定,满足2£ L £ U £ 1 000 000 000且0£ U – L £ 1 000 000。[/align]
输出格式
输出到文件divisors.out。对于给定的范围,输出该范围内约数个数D最多的数P。若有多个,则输出最小的那个。请输出”Between L and U, P has a maximum of D divisors.”,其中L, U, P和D的含义同前面所述。
样例输入与输出
【算法】
本题的要求是,求出一个给定区间内的含约数最多的整数。
首先明确一下如何求一个数的约数个数:若一个数N满足:N = A1N1 * A2N2 * A3N3 * …… * AmNm,则n的约数个数为(N1 + 1) (N2 + 1) (N3 + 1) …… (Nm + 1)。这是可以用乘法原理证明的。
最浅显的算法是,枚举区间内的每个整数,统计它们的约数个数。这个算法很容易实现,但是时间复杂度却相当高。因为题目约定区间的最大宽度是100,000,而整数的范围是1~1,000,000,000,都相当庞大。因此,在极限规模时,时间是无法忍受的。所以,我们需要尽量的优化时间。
分析一下枚举的过程就会发现,如果我们枚举到两个数n和m*n(m为一相对于n较大的质数),那么我们将重复计算n的约数两次。据此,我们发现了枚举效率低的根本所在。为了解决这一重复,我们可以选取另一种搜索方法——以质因子为对象进行搜索。
初始时,令number := 1,然后从最小的质数2开始枚举,枚举包含一个2、两个2……n个2的情况……直至number * 2n大于区间的上限(max)。对于每种“2的情况”,令number := number * 2n,再枚举3的情况,然后,枚举5的情况、7的情况……方法相同。整个过程是一个深度搜索的过程。当number大于等于区间下限(min)时,我们就找到了一个区间内的数,根据前面介绍的方法,可以得到它的约数个数。所有的区间内的数的约数个数的最大值就是我们要求的目标。
值得注意的是,我们枚举过程中得到的number可能无用的,即无论用number去乘以多少,都无法得到区间内的数。这样的number如果继续枚举下去,无疑会大大降低效率。那么,能否通过简单的判断,将其剪去呢?答案是可以的。很容易证明,如果(min – 1) div number < max div number,则区间内存在可以被number整除的数。因为,如果区间[min, max]内存在可以被number整除的数,也即是从min到max中至少有一个数能被number整除,那么区间[min – 1, max]内的数被number除得的商肯定不止一种,所以(min – 1) div number必然小于max div number。我们只需枚举符合要求的number;至于不符合的,可以剪去。
此外,我们枚举的质数可能会达到很大。因为给出的整数最大可以达到1,000,000,000,它的质因数自然最大也可以到100,000,000的数量级。如果按上面的方法枚举,显然无法承受时间的压力。但是,我们又可以看到,对于区间内的任一整数,它包含的大于SQRT(1,000,000,000) = 31623的质因数最多只可能有一个。因此,我们只需枚举小于31623的质数。如果对一个符合要求的number(即,可以证明区间内至少存在一个数可以被number整除),无法找到一个小于31623的质数p使得number * p * x ∈ [min, max](x为一正整数)。那么,可知number需要乘以一个大于31623的质数才能得到number’,使得number’ ∈ [min, max]。根据前面介绍的方法,只需将number包含的约数个数乘以2,即得number’包含的约数个数。
我们还能看到,如果当前搜索状态为(from, number, total),其中from是指当前枚举到的质因子(按从小到大枚举),total是指number中包含的约数个数。那么剩下的因子数最多为q = [logfrom(max / number)],这些因子组成的约数个数(即上述求约数个数时用到的一串乘积)最大为2q。当前所能取到的(理想情况)最大约数个数就是total * 2q,如果这个数仍然无法超过当前最优解,则这一分支可以剪去。
深度搜索的过程,从表面上看是一个指数级的复杂度。其实不然,更准确的说,复杂度应为O(plogn)。因为,它的指数增长速度是随n成对数级增长,本质上说,还是多项式级的算法。而且我们还进行了一些剪枝,由于枚举中的分支多数为非法的,因此通过剪枝可以去掉绝大多数的分支。这样就大大提高了程序的效率。尽管数据规模很大,但对于极限数据仍然可以做到“一闪即出”。
【程序】
program divisors(input, output);
const fin = 'divisors.in';
fon = 'divisors.out';
maxprime = 31622; {质数上限}
amount = 3401; {质数个数}
var primes :array [1..amount] of word; {质数表}
l, {下界}
u, {上界}
number :longint; {保存当前含因数最多的数}
max :word; {因数个数}
procedure getprimes; {求质数过程(筛法)}
var get :array [2..maxprime] of boolean;
i, j :word;
begin
fillchar(get, sizeof(get), 1);
for i := 2 to maxprime do
if get[i]
then begin
j := i + i;
while j <= maxprime do
begin
get[j] := false;
inc(j, i)
end
end;
j := 0;
for i := 2 to maxprime do
if get[i]
then begin
inc(j);
primes[j] := i
end
end;
procedure try(from, tot :word; num, low, up :longint);
{搜索过程:
from: 当年枚举的质因数
tot: 当前质因数个数
num: 当前数(number)
low: 当前下界
up: 当前上界
}
var x, y, n, m :longint;
i, j, t :word;
begin
if num >= l {判断number是否在[l,u]区间内}
then if (tot > max) or ((tot = max) and (num < number))
then begin {判断number是否优于当前最优解}
max := tot;
number := num {更新当前最优解}
end;
if (num < up) and (tot shl 1 > max) then max := tot shl 1;
{判断并处理包含大质因数的数}
for i := from to amount do {枚举质因子}
if primes[i] > up
then exit
else begin
j := primes[i];
x := low - 1; y := up; n := num; t := tot; m := 1;
{初始化}
while true do
begin {枚举质因子个数}
inc(m); inc(t, tot);
x := x div j; y := y div j;
if x = y then break;
n := n * j;
try(i + 1, t, n, x + 1, y) {搜索下一层}
end;
if tot < max shr m then exit {剪枝}
end
end;
begin
assign(input, fin); reset(input);
assign(output, fon); rewrite(output);
getprimes; {初始化,求出质数表}
readln(l, u); {读入下界和上界}
if (l = 1) and (u = 1) {判断特例}
then begin
max := 1;
number := 1
end
else begin
max := 2; number := l; {初始化}
try(1, 1, 1, l, u) {搜索}
end;
writeln('Between ', l, ' and ', u, ', ', number, ' has a maximum of ', max, ' divisors.');
{输出}
close(output); close(input)
end.
上面的代码不知道是什么语言,但思路和王晓东老师给的代码思路一样,所以下面王晓东老师的代
码实现:
Code:
#include<iostream>
using namespace std;
#define max Max
const long MAXP = 100000;
long prim[MAXP];
long max, numb, PCOUNT; //max存放最多约数个数,numb存放约数个数最多的数
void primes(); //用筛选法产生质数存于prim数组中
long l, u;
void search(long from, long tot, long num, long low, long up);
int main()
{
primes();
cin >> l >> u;
if ((l == 1) && (u == 1))
{
max = 1;
numb = 1;
}
else
{
max = 2;
numb = l;
search(1, 1, 1, l, u);
}
cout << max << endl << numb << endl;
system("pause");
return 0;
}
void primes()
{
bool get[MAXP+1];
long i;
for (i = 2; i <= MAXP; i++)
get = true;
for (i = 2; i <= MAXP; i++)
if (get[i])
{
long j = i + i;
while (j <= MAXP)
{
get[j] = false;
j += i;
}
}
long ii, j;
for (ii = 2, j = 0; ii <= MAXP; ii++)
if (get[ii]) prim[++j] = ii;
PCOUNT = j;
}
// 区间[low,up]上,tot为当前约数最多个数,num为约数个数最多的数,
//from表示现在是第几个质数。
void search(long from, long tot, long num, long low, long up)
{
if (num >= l)
if ( (tot > max) || ((tot == max) && (num < numb)) )
{
max = tot;
numb = num;
}
if ((low == up) && (low > num)) search(from, tot*2, num*low, 1, 1);
for (long i = from; i <=PCOUNT; i++)
{
if (prim[i] > up) return;
else
{
long j = prim[i], x = low - 1, y = up, n = num, t = tot, m = 1;
while (true)
{
m++;
t += tot;
x /= j;
y /= j;
if (x == y) break;
n *= j;
search(i+1, t, n, x+1, y);
}
m = 1 << m;
if (tot < max / m) return;
}
}
}
下面还贴一个图片解释:在网上找的哦,呵呵
但是不会用,我觉得CSDN传图片的功能好奇怪,不太好搞,算了,不传了,给大家一个博客地址,可以去看看~~
http://hi.baidu.com/1314lingxq/blog/item/5b545916e47dda5ef819b8fa.html
如果大家有什么好的想法,一定写在下面哦,这个问题真的很让人纠结~~~
[align=center]Divisors解题报告[/align]
[align=center]苏州中学 戴文渊[/align]
【问题】
数学家们喜欢各种类型的有奇怪特性的数。例如,他们认为945是一个有趣的数,因为它是第一个所有约数之和大于本身的奇数。
为了帮助他们寻找有趣的数,你将写一个程序扫描一定范围内的数,并确定在此范围内约数个数最多的那个数。不幸的是,这个数和给定的范围的都比较较大,用简单的方法寻找可能需要较多的运行时间。所以请确定你的算法能在几秒内完成最大范围内的扫描。
输入格式
[align=left]从文件divisors.in输入。文件只有一行,给出扫描的范围,由下界[i]L和上界U确定,满足2£ L £ U £ 1 000 000 000且0£ U – L £ 1 000 000。[/align]
输出格式
输出到文件divisors.out。对于给定的范围,输出该范围内约数个数D最多的数P。若有多个,则输出最小的那个。请输出”Between L and U, P has a maximum of D divisors.”,其中L, U, P和D的含义同前面所述。
样例输入与输出
[align=center]divisors.in[/align] | [align=center]divisors.out[/align] |
[align=center]1 10[/align] | Between 1 and 10, 6 has a maximum of 4 divisors. |
[align=center]1000 1000[/align] | Between 1000 and 1000, 1000 has a maximum of 16 divisors. |
[align=center]999999900 1000000000[/align] | Between 999999900 and 1000000000, 999999924 has a maximum of 192 divisors. |
本题的要求是,求出一个给定区间内的含约数最多的整数。
首先明确一下如何求一个数的约数个数:若一个数N满足:N = A1N1 * A2N2 * A3N3 * …… * AmNm,则n的约数个数为(N1 + 1) (N2 + 1) (N3 + 1) …… (Nm + 1)。这是可以用乘法原理证明的。
最浅显的算法是,枚举区间内的每个整数,统计它们的约数个数。这个算法很容易实现,但是时间复杂度却相当高。因为题目约定区间的最大宽度是100,000,而整数的范围是1~1,000,000,000,都相当庞大。因此,在极限规模时,时间是无法忍受的。所以,我们需要尽量的优化时间。
分析一下枚举的过程就会发现,如果我们枚举到两个数n和m*n(m为一相对于n较大的质数),那么我们将重复计算n的约数两次。据此,我们发现了枚举效率低的根本所在。为了解决这一重复,我们可以选取另一种搜索方法——以质因子为对象进行搜索。
初始时,令number := 1,然后从最小的质数2开始枚举,枚举包含一个2、两个2……n个2的情况……直至number * 2n大于区间的上限(max)。对于每种“2的情况”,令number := number * 2n,再枚举3的情况,然后,枚举5的情况、7的情况……方法相同。整个过程是一个深度搜索的过程。当number大于等于区间下限(min)时,我们就找到了一个区间内的数,根据前面介绍的方法,可以得到它的约数个数。所有的区间内的数的约数个数的最大值就是我们要求的目标。
值得注意的是,我们枚举过程中得到的number可能无用的,即无论用number去乘以多少,都无法得到区间内的数。这样的number如果继续枚举下去,无疑会大大降低效率。那么,能否通过简单的判断,将其剪去呢?答案是可以的。很容易证明,如果(min – 1) div number < max div number,则区间内存在可以被number整除的数。因为,如果区间[min, max]内存在可以被number整除的数,也即是从min到max中至少有一个数能被number整除,那么区间[min – 1, max]内的数被number除得的商肯定不止一种,所以(min – 1) div number必然小于max div number。我们只需枚举符合要求的number;至于不符合的,可以剪去。
此外,我们枚举的质数可能会达到很大。因为给出的整数最大可以达到1,000,000,000,它的质因数自然最大也可以到100,000,000的数量级。如果按上面的方法枚举,显然无法承受时间的压力。但是,我们又可以看到,对于区间内的任一整数,它包含的大于SQRT(1,000,000,000) = 31623的质因数最多只可能有一个。因此,我们只需枚举小于31623的质数。如果对一个符合要求的number(即,可以证明区间内至少存在一个数可以被number整除),无法找到一个小于31623的质数p使得number * p * x ∈ [min, max](x为一正整数)。那么,可知number需要乘以一个大于31623的质数才能得到number’,使得number’ ∈ [min, max]。根据前面介绍的方法,只需将number包含的约数个数乘以2,即得number’包含的约数个数。
我们还能看到,如果当前搜索状态为(from, number, total),其中from是指当前枚举到的质因子(按从小到大枚举),total是指number中包含的约数个数。那么剩下的因子数最多为q = [logfrom(max / number)],这些因子组成的约数个数(即上述求约数个数时用到的一串乘积)最大为2q。当前所能取到的(理想情况)最大约数个数就是total * 2q,如果这个数仍然无法超过当前最优解,则这一分支可以剪去。
深度搜索的过程,从表面上看是一个指数级的复杂度。其实不然,更准确的说,复杂度应为O(plogn)。因为,它的指数增长速度是随n成对数级增长,本质上说,还是多项式级的算法。而且我们还进行了一些剪枝,由于枚举中的分支多数为非法的,因此通过剪枝可以去掉绝大多数的分支。这样就大大提高了程序的效率。尽管数据规模很大,但对于极限数据仍然可以做到“一闪即出”。
【程序】
program divisors(input, output);
const fin = 'divisors.in';
fon = 'divisors.out';
maxprime = 31622; {质数上限}
amount = 3401; {质数个数}
var primes :array [1..amount] of word; {质数表}
l, {下界}
u, {上界}
number :longint; {保存当前含因数最多的数}
max :word; {因数个数}
procedure getprimes; {求质数过程(筛法)}
var get :array [2..maxprime] of boolean;
i, j :word;
begin
fillchar(get, sizeof(get), 1);
for i := 2 to maxprime do
if get[i]
then begin
j := i + i;
while j <= maxprime do
begin
get[j] := false;
inc(j, i)
end
end;
j := 0;
for i := 2 to maxprime do
if get[i]
then begin
inc(j);
primes[j] := i
end
end;
procedure try(from, tot :word; num, low, up :longint);
{搜索过程:
from: 当年枚举的质因数
tot: 当前质因数个数
num: 当前数(number)
low: 当前下界
up: 当前上界
}
var x, y, n, m :longint;
i, j, t :word;
begin
if num >= l {判断number是否在[l,u]区间内}
then if (tot > max) or ((tot = max) and (num < number))
then begin {判断number是否优于当前最优解}
max := tot;
number := num {更新当前最优解}
end;
if (num < up) and (tot shl 1 > max) then max := tot shl 1;
{判断并处理包含大质因数的数}
for i := from to amount do {枚举质因子}
if primes[i] > up
then exit
else begin
j := primes[i];
x := low - 1; y := up; n := num; t := tot; m := 1;
{初始化}
while true do
begin {枚举质因子个数}
inc(m); inc(t, tot);
x := x div j; y := y div j;
if x = y then break;
n := n * j;
try(i + 1, t, n, x + 1, y) {搜索下一层}
end;
if tot < max shr m then exit {剪枝}
end
end;
begin
assign(input, fin); reset(input);
assign(output, fon); rewrite(output);
getprimes; {初始化,求出质数表}
readln(l, u); {读入下界和上界}
if (l = 1) and (u = 1) {判断特例}
then begin
max := 1;
number := 1
end
else begin
max := 2; number := l; {初始化}
try(1, 1, 1, l, u) {搜索}
end;
writeln('Between ', l, ' and ', u, ', ', number, ' has a maximum of ', max, ' divisors.');
{输出}
close(output); close(input)
end.
相关文章推荐
- 最大约数问题(详细代码)
- NekoHTML解析HTML为XML后TagName一直为大写的问题解决
- JAVA代码—算法基础:最大数问题
- 混淆代码问题之没有jar包的解析Error:Execution failed for task ':app:transformClassesAndResourcesWithProguardForRel
- jQuery使用ajax传输xml文档在IE中无法正确解析的问题
- 最大子数组问题-c++代码实现及运行实例结果
- 最大子数组问题-暴力求解-c++代码实现及运行实例结果
- Erlang递归的性能问题:写出正确的尾递归代码
- 关于求数组的最大值和最小值问题(C代码)
- loadrunner11 因为本机装了360 firefox ie chrome等浏览器 导致录制的vugen代码一直只有return 0
- 一个问题阻止windows正确检查此机器的许可证。错误代码0x80070002
- 代码来解析MRC问题
- 【Java并发编程】之十八:第五篇中volatile意外问题的正确分析解答(含代码)
- 混淆代码问题之没有jar包的解析Error:Execution failed for task ':app:transformClassesAndResourcesWithProguardForRel
- 【算法设计】最大子段和问题解析(对应算法第三题)
- 最大子数组问题-c++代码实现及运行实例结果
- 最大子数组问题-暴力求解-c++代码实现及运行实例结果
- 突然发现这个VSPaste插件粘贴过来的代码居然有白色背景,还只有一部分有,好纠结。。。
- 六种姿势拿下连续子序列最大和问题,附伪代码(以HDU 1003 1231为例)
- 翻煎饼问题(代码及解析)