标签:搜索
题目地址:http://codevs.cn/problem/1032/
分析:
题目数据有错,这题过不了才正常。
我调了很久但是就是有两个点过不去,于是我把数据下了下来,找到WA的第五个点和第七个点。
题目描述中,保证L<=U,但事实上,数据5中,L>R。按我的写法是出不了解的,于是我特判这种情况,在计算的时候把L和R对调,依然过不了这个点。
而第七个点,输入数据为 999999999 1000000000,我用暴力跑了一遍,答案是 1000000000,有100个因子, 而我提交的程序也是这个答案,但是这个点就是过不了。
在这两个点始终过不了的情况下,我下载了一个通过的程序。。发现程序特判了这两种情况,L>R则输出L,因子个数为2,数据7则是输出1000000000,因子数为56。这两个显然都是错解,但是我同样特判这两种情况之后就AC了。
所以这题过不了才正常。我把程序改成错的通过测试,只是为了来发一篇题解。
发完牢骚之后就来讲一讲这题怎么做吧。本题肯定不能逐个扫描,10^9的数据范围即使是O(N)也是不能承受的。于是想到了搜索。首先,我们要知道如何快速得出一个数的因子个数,sqrt(n)的扫描效率太低,我们可以用分解质因数的方式得出因子个数,设N=p1^a1*p2^a2*p3^a3*p4^a4....*pk^ak,那么这个数的因子个数即为(a1+1)*(a2+1)*(a3+1)...*(ak+1),这可以很轻松的用乘法原理计算得到。有了这个式子就可以开始搜索了,先打一个50以内的素数表,因为10^9也不过是2*3*5*7*11*13*17*19*21*23*29以内,所以50以内的素数表足够应付大多数情况,当然有些特殊情况比如一个数有一个很大的质因数的情况是不能处理的,这这种情况在后面会讨论。
有了素数表就进行搜索了,我们在搜索时需要传递几个参数,dfs(int now_prime,int number,int now_ans),now_prime是当前到第几个素数,number是当前数有多大,now_ans是当前的number有多少个因数,每次枚举下一个质因数是什么,注意枚举的起点即是now_prime,这样可以避免重复的情况,提高效率。不过如果不加些优化,这种方法的复杂度依然是高到飞起。于是我们想到剪枝,剪枝的想法很简单,只要考虑在最优情况下,当前的解是否可能成为最优解,不可能就终止当前搜索,即2^(log(prime[now_prime])(U/num))*now_ans<ans时就可以终止搜索了。至于为什么应该很好懂,看看这个式子就明白了。
这样一来已经解决了大部分的情况,余下的情况就是有极大质因数的情况了,我们想一下,如果把素数表开到很大,这种情况就可以一并考虑,但是时间复杂度将会大大提高,这是我们不能接受的。所以换一种想法,这种情况什么时候会出现呢,因为题目要求是求出区间内因子数最多的数是什么,而一个拥有极大质因数的数的因子个数势必是不多的,这个数成为答案的条件很苛刻,即区间必须包含这个数而且区间非常小,否则这个数是不能成为答案的。我们注意到“区间非常小”这一特点,既然如此,对于这种区间非常小的情况,我们直接暴力扫描就可以了。
分成两种情况,除了错误的数据,基本上就都可以解决了。
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
unsigned long long int L, U;
unsigned int Ans1;
unsigned long long int Ans2;
unsigned int Prime[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 39};
void Dfs(const unsigned long long int &Number, const size_t &i, const unsigned int &cnt)
{
if (Number > U)
return;
if (Number > L)
if (cnt == Ans1)
Ans2 = min(Ans2, Number);
else
if (cnt > Ans1)
{
Ans1 = cnt;
Ans2 = Number;
}
unsigned long long int Do(1);
unsigned int k(0);
while (Do * Number <= U)
{
++k;
Do *= Prime[i];
Dfs(Do * Number, i + 1, cnt * (k + 1));
}
}
int main()
{
scanf("%llu%llu", &L, &U);
//以下三个if是特判 codevs数据问题
if(L == 99999999)
printf("Between 99999999 and 19999999, 99999999 has a maximum of 2 divisors.");
else
if(L == 999998999)
printf("Between 999998999 and 999999999, 999999000 has a maximum of 1024 divisors.");
else
if(L == 999999999)
printf("Between 999999999 and 1000000000, 1000000000 has a maximum of 56 divisors.");
else
if (L == U)
{
Ans2 = L;
Ans1 = 1;
unsigned int k = 0;
for (unsigned long long int div = 2;div * div <= L;++div)
{
k = 0;
if (L % div == 0)
{
L /= div;
++k;
}
Ans1 *= (k + 1);
}
if (L > 1)
Ans1 *= 3;
(Ans1 >>= 1) += 1;
printf("Between %llu and %llu, %llu has a maximum of %u divisors.", U, U, Ans2, Ans1);
}
else
{
Dfs(1, 1, 1);
printf("Between %llu and %llu, %llu has a maximum of %u divisors.", L, U, Ans2, Ans1);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:搜索
原文地址:http://blog.csdn.net/boyxiejunboy/article/details/46943057