POJ2689 Prime Distance [线性筛]

it2022-05-05  173

POJ2689 Prime Distance [线性筛]

写在最前面:这是一道因为各种原因被磨了很久的题,今天重新拿出来做,排bug排崩溃

题目:http://poj.org/problem?id=2689 题目大意:给定一个区间[L,R],求出该区间内相邻两个质数中,距离最近的两个和距离最远的两个 数据范围:0<L<R<2的31次方 R-L<1000000(1e6) 样例: 输入: 2 17 14 17 输出: 2,3 are closest, 7,11 are most distant. There are no adjacent primes.

思路: 2的31次方的数据过大,无法直接用筛法筛到R为止 但是题目中特意告诉了我们R-L的范围,并且数据范围不大,也就是说我们可以先求出L到R这些数的因子,再用最朴素的算法,将L到R的合数全部进行标记 引理:任何一个合数n,都有不超过sqrt(n)的因子 所以我们只需要将数筛到sqrt(n)即可,大大缩短了时间

线性筛

数组v[i]存储i的最小质因子 prime从小到大存储质数

void sha(int n) { m = 0; for (int i(2); i <= n; ++i) { if (v[i] == 0) { prime[++m] = i; v[i] = i; } for (int j(1); j <= m; ++j) { if (i*prime[j] > n || prime[j] > v[i]) break; v[i*prime[j]] = prime[j]; } } }

朴素算法标记[L,R]区间内的合数

节约空间,此时将v数组用来标记是否为合数

for (int i(1); i <= m; ++i) { if (prime[i]*prime[i] > r) break; for (int j=l/prime[i]; j <=r/prime[i]; ++j) { if ((j == 1) || (j*prime[i]-l < 0)) continue; //如果j == 1计入在内,则会把质数本身标记为合数 //j*prime[i]-l >= 0,要保证下标不过界 v[j*prime[i]-l] = 1;//L,R范围太大,为节省下标空间,把L当做0,进行标记 } }

朴素算法找出最大值和最小值

数据比较小,所以直接两层for没有太大的时间问题

long long d1,d2; long long dmax; dmax = 0; long long c1,c2; long long cmin; cmin = r-l; long long p1,p2; p1 = -1; p2 = -1; for (long long i(0); i <= (r-l); ++i) { if (!v[i]) { p1 = p2; p2 = i; if ((p2 - p1) > dmax && (p1 != -1) && (p2 != -1)) { d1 = p1; d2 = p2; dmax = p2-p1; } if ((p2 - p1) < cmin && (p1 != -1) && (p2 != -1)) { c1 = p1; c2 = p2; cmin = p2-p1; } } } if (dmax == 0) printf("There are no adjacent primes.\n"); else printf("%lld,%lld are closest, %lld,%lld are most distant.\n",c1+l,c2+l,d1+l,d2+l);

完成! 提交! AC! 等等 AC WA!!!!!

为什么呢???

排bug的辛酸史

当我们输入13, 17时,我们发现 咦???为什么是-4617218172吧啦吧啦,难道没有更新么 查看cmin最初的大小以及每次更新时大小是如何比较的

cmin = r-l; if ((p2 - p1) < cmin && (p1 != -1) && (p2 != -1)) { c1 = p1; c2 = p2; cmin = p2-p1; }

cmin = 17-13 =4 p2-p1 = 17-13 = 4 = cmin 没有更新过c1, c2的值,所以此时,应该把cmin的初始值再设大一点,+1即可

再提交!

AC

WA

还有啥bug没排啊,自闭了

输入数据:1, 12345 咦??? 1怎么算质数进去了

for (int i(1); i <= m; ++i) { if (prime[i]*prime[i] > r) break; for (int j=l/prime[i]; j <=r/prime[i]; ++j) { if ((j == 1) || (j*prime[i]-l < 0)) continue; v[j*prime[i]-l] = 1; } }

仔细看朴素标记合数的代码,发现L=1时,v[0]根本没有进循环,所以默认为初始值v[0]=0,与其他质数的状态恰好相同 那么就把这种情况单独分出来考虑 增加代码

if (l == 1) v[0] = 1;

第三次提交

AC

AC!!!

排bug的时候,一定要注意特殊的情况 像这道题,特殊情况,可能是 区间内没有质数 区间内只有一个质数 区间内的唯一质数=L或者R L或者 R是质数??? L=1的时候‘ 这些都是比较容易出错的几个点 出现WA的时候注意一下

还有这道题目 我不断出现WA的情况,并且正常的L,R的数据都是正确的情况下(那时候没有想到L=1的情况,也没有试到有且仅有两个质数且分别等于L,R的情况) 我一度以为是我的输出问题,还看了好久,到底是空格漏了还是字母打错了,校对到怀疑人生 可能是我比较蠢

完整代码:

//poj2689 #include <cstdio> #include <cstring> #include <math.h> using namespace std; const int MAXN = 1123456; const int rsqrt = 65536; long long v[MAXN]; long long prime[rsqrt]; int m; void sha(int n); int main() { //多组数据,需要多次重复计算,所以可以直接现将所有素数筛出来 sha(rsqrt); long long l, r; while (scanf("%lld%lld", &l, &r) == 2) { memset(v, 0, sizeof(v)); for (int i(1); i <= m; ++i) { if (prime[i]*prime[i] > r) break; for (int j=l/prime[i]; j <=r/prime[i]; ++j) { if ((j == 1) || (j*prime[i]-l < 0)) continue; v[j*prime[i]-l] = 1; } } if (l == 1) v[0] = 1;// long long d1,d2; long long dmax; dmax = 0; long long c1,c2; long long cmin; cmin = r-l+1; // long long p1,p2; p1 = -1; p2 = -1; for (long long i(0); i <= (r-l); ++i) { if (!v[i]) { p1 = p2; p2 = i; if ((p2 - p1) > dmax && (p1 != -1) && (p2 != -1)) { d1 = p1; d2 = p2; dmax = p2-p1; } if ((p2 - p1) < cmin && (p1 != -1) && (p2 != -1)) { c1 = p1; c2 = p2; cmin = p2-p1; } } } if (dmax == 0) printf("There are no adjacent primes.\n"); else printf("%lld,%lld are closest, %lld,%lld are most distant.\n",c1+l,c2+l,d1+l,d2+l); } return 0; } void sha(int n) { m = 0; for (int i(2); i <= n; ++i) { if (v[i] == 0) { prime[++m] = i; v[i] = i; } for (int j(1); j <= m; ++j) { if (i*prime[j] > n || prime[j] > v[i]) break; v[i*prime[j]] = prime[j]; } } }

最新回复(0)