地址:http://acm.nyist.net/JudgeOnline/problem.php?pid=24
素数距离问题
时间限制:3000 ms | 内存限制:65535 KB
难度:2
-
描述
-
现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度。如果左右有等距离长度素数,则输出左侧的值及相应距离。
如果输入的整数本身就是素数,则输出该素数本身,距离输出0-
输入
-
第一行给出测试数据组数N(0<N<=10000)
接下来的N行每行有一个整数M(0<M<1000000),
输出
-
每行输出两个整数 A B.
其中A表示离相应测试数据最近的素数,B表示其间的距离。
样例输入
-
3 6 8 10
样例输出
-
5 1 7 1 11 1
-
第一行给出测试数据组数N(0<N<=10000)
思路:
一开始想到的是用一个素数表来存储,用猜想6N+-1的原则,只需要算到6N+5的时候,就足够判断是中间,左边,还是右边了。用到厄拉多塞筛法,两个for,一步步乘以质数,将非质数排除,最后直接往两边找就可以了。结果全超时了。。
原本想用sqrt,想着重复算sqrt(左,中,右)会比较慢。结果看到讨论区,用这种方法做c的,能ac。我也试着用java,发现也可以。很费解,原以为sqrt慢,结果更快了。
无奈之下,只好演算下,发现3*sqrt(m)[最好情况]总是会小于m+5的,加上讨论区,有人把所以范围内的素数输出来,发现,绝大部分距离在0-2之间。当m=10000时,假设5*sqrt(m)也会小于m+5.总是会存储素数表的快。
这是讨论区的,将c改java
public static void sqrtPrimesMinDistance1(int m) {
int tmpR, tmpL, minR, minL;
minR = minL = 0;
tmpR = tmpL = m;
while (true) {
if (isPrimes(tmpL)) {
System.out.println(m - minL + " " + minL);
break;
}
if (isPrimes(tmpR)) {
System.out.println(m + minR + " " + minR);
break;
}
tmpL--;
tmpR++;
minL++;
minR++;
}
}
原本以为他没有考虑到左距离和右距离的比较久直接跳出了,还以为是测试数据有bug。后面才发现,这里设计得很巧妙。用了两个if,先判左边,如果右边相同就取左边,若小也取左边,也刚好就break。两个符合情况,就判下一个右边的if。结果就很符合题意了。
而我写的
public static void sqrtPrimesMinDistance2(int m) {
int tmpR, tmpL, minR, minL;
minR = minL = m;
tmpR = tmpL = m;
if (isPrimes(m)) {
System.out.println(m + " 0");
return;
}
while (true) {
if (isPrimes(tmpL)) {
minL = m - tmpL;
}
if (isPrimes(tmpR)) {
minR = tmpR - m;
}
if (minR != m || minL != m) {
if (minL <= minR)
System.out.println(tmpL + " " + minL);
else
System.out.println(tmpR + " " + minR);
break;
}
}
}
先得到值后,再做后面的判断,多此一举,结果还超时了。
最后附上源码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-- > 0) {
int m = sc.nextInt();
// findPrimesMinDistance(m);
sqrtPrimesMinDistance1(m);
}
}
public static boolean isPrimes(int m) {
// 特殊值判断
if (m == 0 || m == 1)
return false;
if (m == 2)
return true;
for (int i = 2; i <= Math.sqrt(m); i++) {
if (m % i == 0)
return false;
}
return true;
}
// 仿造讨论区的一模一样,318 923,本以为没有考虑到前后谁到谁小的情况就直接跳出了,后来发现很巧妙设计
public static void sqrtPrimesMinDistance1(int m) {
int tmpR, tmpL, minR, minL;
minR = minL = 0;
tmpR = tmpL = m;
while (true) {
if (isPrimes(tmpL)) {
System.out.println(m - minL + " " + minL);
break;
}
if (isPrimes(tmpR)) {
System.out.println(m + minR + " " + minR);
break;
}
tmpL--;
tmpR++;
minL++;
minR++;
}
}
// 通过存储范围内的素数表,直接取值,还超时。
// 想一想也有可能,用素数表,就要算m+5(除0/1)次,用sqrt,算的是中间+左+右-->3*sqrt(m)次,
// 演算了下,发现真的是sqrt计算量更小
public static void findPrimesMinDistance(int m) {
int[] primes = new int[m + 5];
for (int i = 2; i < primes.length; i++) {
if (primes[i] == 0)
for (int k = 2; k * i < primes.length; k++) {
primes[k * i] = 1; // 非质数都为1
}
}
if (primes[m] == 0) // 本身是质数
System.out.println(m + " 0");
else {
int minDistance = 0;
int tmpR = m;
// 向右找
while (++tmpR < primes.length) {
if (primes[tmpR] == 0) {
minDistance = tmpR - m;
break;
}
}
// 向左找
int tmpL = m;
while (--tmpL > 0) {
if (primes[tmpL] == 0) {
if (m - tmpL <= minDistance) { // <=都是取到左边的
minDistance = m - tmpL;
} else
tmpL = tmpR;
break;
}
}
System.out.println(tmpL + " " + minDistance);
}
}
}