P7933 [COCI2007-2008#5] PASCAL 题解
这题非常有意思。
思路和代码
首先观察那份 Pascal 代码,由于 Pascal 代码非常好懂,即使是 C++ 党也能轻易看懂,所以这里不再赘述分析过程。我们可以转出下面的代码:
for(int i=n-1;i>=1;i--){
cnt++;
if(n%i==0){
break;
}
}
cout<<cnt<<endl;
但我们容易发现这玩意儿的时间复杂度依赖这个数的最大非本身因数,这可不太行,如果输入一个大质数就爆掉了。
同时可以搞一点简单推导,发现break;
的时候,\(i\) 应该是 \(n\) 的最大非本身因数,那 \(cnt\) 应该就是 \(n-i\)。记录 \(f(n)\) 表示 \(n\) 的最大非本身因数,我们要计算的就是 \(n-f(n)\)。
然后发现有点小问题,\(1\) 没有答案,但原代码输出了 \(0\),所以如果没有找到答案就要puts("0");
。
所以可以把 \(cnt\) 去掉了:
for(int i=n-1;i>=1;i--){
if(n%i==0){
cout<<n-i<<endl;
return 0;
}
}
puts("0");
还是注意如果没有找到答案要puts("0");
。
因为算 \(f(n)\) 现在是最坏 \(O(n)\),所以要优化。
仔细一想,如果枚举到的 \(i>\lfloor\frac{n}{2}\rfloor\) 是不可能贡献为答案的,所以改变枚举范围:
for(int i=n/2;i>=1;i--){
if(n%i==0){
cout<<n-i<<endl;
return 0;
}
}
puts("0");
这样的时间复杂度是 \(O(\lfloor\frac{n}{2}\rfloor)\),但是输入一个非常趋近 \(10^9\) 的质数,即使在洛谷神机上还是跑不过,所以再优化。
考虑枚举从 \(2\) 开始到 \(\sqrt{n}\),第一个可以把 \(n\) 整除的数 \(i\),它所对应的另一个因数 \(\frac{n}{i}\) 一定是正确的 \(f(n)\)。
于是得到以下代码:
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
cout<<n-(n/i)<<endl;
return 0;
}
}
然后就光荣地 WA 了两个。
为什么?我们容易发现,一个质数所对应的 \(f(n)=1\),但是这里我们并没有枚举到 \(n\)。
所以可以写一个判断质数的函数,如果这个数没有找到答案,说明这个数不是质数就是 \(1\),直接判断就行:
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
cout<<n-(n/i)<<endl;
return 0;
}
}
if(isprime(n)){//判断n是否是一个质数
cout<<n-1<<endl;
}
else{
puts("0");//此时一定有n=1,输出0
}
这样做时间复杂度是 \(O(\sqrt{n})\) 的,能够 AC。