题目:
X轴上个点,从左到右依次编号为0到,相邻点距离为1,其中有n个点有特殊意义,从小到大依次为到,其中保证=0.
现在需要建设收集站,有两个要求必须被满足:
1、每个有意义的点必须修建收集站。
2、相邻收集站的距离必须为1或为某个质数。
现给出n和a数组,求需要建设收集站的最小数量。
方法一:哥德巴赫猜想+欧拉筛选法构造素数表
站点数初始化为n,关键是要判断一个非素数可以分解成几个素数
这里就得用到哥德巴赫猜想:
- 一个数n是大于2的偶数时,最少可以分解为2个素数
- 一个数n是大于等于5的奇数时:如果n-2是素数,则n可以分解为最少2个素数
否则,n可以分解为最少3个素数
欧拉筛选法构造素数表:
构造一个从2到之间的素数表,具体如何构造呢?
从2开始到max,visit数组记录数是否已经被访问过,未访问过则加入素数表,再把prime里面记录的素数,升序来当做要消去合数的最小素因子,被消去的合数记录为被访问过,因为是用最小素因子来消去合数,为了避免重复消去合数,当 i是的倍数时,,如果继续运算 ,这里是最小的素因子,当时会重复消去,所以才跳出循环。
举个例子 :,如果不跳出循环,,在时会计算,这样就会造成重复计算,欧拉筛法的原理是通过最小素因子来消除。
、
import java.util.*; public class Solution { /** * * @param n int整型 * @param a int整型一维数组 * @return int整型 */ ArrayList<Integer>prime=new ArrayList<>(); public int work (int n, int[] a) { // write code here if(n==1)return 1; int max=0; int[]diff=new int[n]; for(int i=1;i<n;i++){ max=Math.max(max,a[i]-a[i-1]); diff[i]=a[i]-a[i-1]; } Prime(max);//构造素数表 int site=n;//每个有意义的点都可以建成收集站 for(int i=1;i<n;i++){ if(diff[i]<=3)continue; if(!prime.contains(diff[i])){ if((diff[i]&1)==0)site+=1;//哥德巴赫猜想,大于2的偶数可以被拆成两个素数 else { if(prime.contains(diff[i]-2))site+=1; else site+=2; } } } return site; } //欧拉筛选法选素数 void Prime(int max){ boolean[]visit=new boolean[max+1];//记录数是否被访问过 for(int i=2;i<=max;i++){ if(!visit[i]){ prime.add(i);//设所有数都为素数 } for(int j=0;j<prime.size()&&i*prime.get(j)<=max;j++){ visit[i*prime.get(j)]=true;//把prime里面记录的素数,升序来当做要消去合数的最小素因子 if(i%prime.get(j)==0) break; } } } }
复杂度:
时间复杂度:构造素数表的时间复杂度为,主函数一层循环,,因此时间复杂度为
空间复杂度:素数表的大小不超过,因此空间复杂度为
()
方法二:哥德巴赫猜想+判断素数
直接判断每个数x是否是素数会更快,素数的定义就是这个数除了1和它本身没有别的因数,那么可以判断从2到x-1,是否有x的因数,但是这样效率很低,其实一个数若可以进行因数分解,那么分解时得到的两个数一定是一个小于等于sqrt(x),一个大于等于sqrt(x),据此,并不需要遍历到x-1,遍历到sqrt(x)即可,因为若sqrt(x)左侧找不到约数,那么右侧也一定找不到约数。
import java.util.*; public class Solution { /** * * @param n int整型 * @param a int整型一维数组 * @return int整型 */ public int work (int n, int[] a) { // write code here if(n==1)return 1; int site=n;//每个有意义的点都可以建成收集站 for(int i=1;i<n;i++){ int diff=a[i]-a[i-1]; if(diff<=3)continue; else if(!isPrime(diff)){//哥德巴赫猜想,大于2的偶数可以被拆成两个素数 if((diff&1)==0)site+=1; else{ if(isPrime(diff-2))site+=1;//奇数减2如果是素数则可以拆成一个素数和2 else site+=2;}//否则可以拆成3个素数 } } return site; } //判断是否是素数 boolean isPrime(int x){ for(int i=2;i*i<=x;i++) if(x%i==0)return false; return true; } }
复杂度:
时间复杂度:isPrime()函数的时间复杂度为 ,因此总的时间复杂度为
空间复杂度:,额外变量的空间复杂度为常数级
()