题意
给定一个数x0,取小于x0的任何一个素数p1。将这个素数扩大到n1倍,使其恰好大于等于x0。然后将扩大后的这个数也就是n1*p1设置为x1。同样的操作,任取一个小于x1的素数p2。将其扩大到n2倍使得他刚好大于等于x1。设这个扩大到n2倍的数设为x2。现在告诉你x2的值,让你输出最小的可能的x0
分析
我们将上面的 叙述用数学表达式写出来就是
p1 < x0 <= p1*n1 = x1; p2 < x1 <= p2*n2 = x2;
然后还有一个条件恰好大于等于。也就是说x1-p1就要小于x2。那么我们又可以进一步缩进x2的范围也就是
x2-p2+1 <= x1 <= x2 同理也有 x1-p1+1 <= x0 <= x1
从第一个不等式可以看到,x1的范围是由p2也就是第二次选出的那个素数所决定的,那我们想x0小那x1也要尽量小。所以我们从他可能的最小值x2-p2+1开始遍历一直到x2。注意这里的p2选的是x2的最大素数因数。在这次遍历中我们已经定下来 x1了,那接下来的问题就是如何用x1来确定x0的最小值。
同理x2推x1的道理,x0的范围也是[x1-p1+1,x1]。很显然此时x0最小肯定是x1-p1+1,这个p1也是x1的最大素数因数。
找到x1和x2的最大素数因数的算法比较巧妙。大体的思路类似于筛选法求素数,设定一个f数组表示的是f[i]表示的是i这个数的最大素数因数。首先定位0,然后从2开始筛选一次所有非素数,也就是不断的加2。那很显然2肯定是被筛选的数的因子且是素数。然后找下一个素数,找法是利用构造的f数组。素数不可能是除1和本身以外的数的因素。所以在f列表里面素数肯定是没有被初始化的也就是0。
先贴出来。
void getp()
{
int n = 2;
while (n < maxn)
{
//num表示的是当前刷新的数,因为一开始的肯定是素数,而下一个刷新的值直接是加n所以直接赋给2*n
int num = n*2;
//用n遍历一遍刷新所有的素数因数的值
while (num < maxn)
{
f[num] = n;
num += n;
}
//找下一个素数
num = n + 1;
while (f[num] != 0 && n < maxn)
{
num++;
}
//找到了赋值给n
n = num;
}
}
完整代码
#include <iostream>
#include <cmath>
#define mina(a,b) a<b?a:b
#define maxn 1000005
using namespace std;
int f[maxn];
void getp()
{
int n = 2;
while (n < maxn)
{
int num = n*2;
while (num < maxn)
{
f[num] = n;
num += n;
}
num = n + 1;
while (f[num] != 0 && n < maxn)
{
num++;
}
n = num;
}
}
int main()
{
getp();
int x2;
while (cin >> x2)
{
int p2 = f[x2];
int x0 = 999999;
int p1 = 0;
for (int i = x2 - p2 + 1; i <= x2; i++)
{
p1 = f[i];
x0 = mina((i - p1 + 1), x0);
}
cout << x0 << endl;
}
return 0;
}