题目:
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()函数的时间复杂度为图片说明 ,因此总的时间复杂度为图片说明
空间复杂度:,额外变量的空间复杂度为常数级
()