素数距离问题--以为存储素数表会更快,结果sqrt更快

地址:http://acm.nyist.net/JudgeOnline/problem.php?pid=24

素数距离问题

时间限制:3000 ms  |  内存限制:65535 KB
难度:2
描述
现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度。如果左右有等距离长度素数,则输出左侧的值及相应距离。
如果输入的整数本身就是素数,则输出该素数本身,距离输出0
输入
第一行给出测试数据组数N(0 接下来的N行每行有一个整数M(0
输出
每行输出两个整数 A B.
其中A表示离相应测试数据最近的素数,B表示其间的距离。
样例输入
3
6
8
10
样例输出
5 1
7 1
11 1

思路:

一开始想到的是用一个素数表来存储,用猜想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);
		}
	}
}






你可能感兴趣的:(ACM)