题意整理
- 数轴上有n个点,这n个点必须建立收集站。
- 如果这n个点之间,相邻两个点间隔不是素数,则必须在中间修建对应数量的收集站,使得所有间隔是素数。
- 求需要建设的收集站的最少数量。
方法一(欧拉筛+动态规划)
1.解题思路
- 首先计算给定n个特殊点的最大间隔距离,最大距离记为max。
- 然后通过欧拉筛统计1到max间的素数,然后动态规划求每个距离需要的站点数。
- 状态定义:表示对应间隔需要建多少个站点。
- 状态初始化:如果是素数,则不需建站点,初始为0。
- 状态转移:如果不是素数,先将状态置为最大的int型整数。枚举每一个间隔,要么修改当前站点数的站点,要么减去对应间隔状态的站点数加一,两者取较小值。
由于题目数据量过大,运行超时。
2.代码实现
import java.util.*; public class Solution { /** * * @param n int整型 * @param a int整型一维数组 * @return int整型 */ public int work (int n, int[] a) { //求最大的相邻收集站间隔 int max=-1; for(int i=1;i<n;i++){ max=Math.max(max,a[i]-a[i-1]); } //初始化dp数组,dp[i]表示对应间隔需要建多少个站点 int[] dp=new int[max+1]; //素数标记数组 boolean[] prime=getPrime(max); for(int i=1;i<=max;i++){ //如果是素数,不需要额外站点 if(prime[i]){ dp[i]=0; } //如果不是素数,根据之前状态,统计所需最少站点数 else{ dp[i]=Integer.MAX_VALUE; for(int j=1;j<=i/2;j++){ dp[i]=Math.min(dp[i],dp[i-j]+1); } } } //将所需站点添加到结果变量 int res=n; for(int i=1;i<n;i++){ int dist=a[i]-a[i-1]; if(!prime[dist]){ res+=dp[dist]; } } return res; } //欧拉筛统计1到n之间所有素数 private boolean[] getPrime(int n) { boolean[] res=new boolean[n+1]; //本题中1当素数看待 res[1]=true; //flag用于标记检测出的合数,如果为0,则说明没有标记为合数,是素数 int[] flag=new int[n+1]; int[] prime =new int[n+1]; //目前统计出的素数数量 int count=0; for(int i =2;i<=n;i++) { if(flag[i]==0){ prime[count++]=i; res[i]=true; } //当前数字i乘以之前统计出的素数,作为合数标记出来 for(int j=0;j<count&&i*prime[j]<=n;j++) { flag[prime[j]*i]=1; } } return res; } }
3.复杂度分析
- 时间复杂度:假设相邻站点最大间隔为d,求dp数组过程总共有两层循环,所以最终的时间复杂度是。
- 空间复杂度:假设相邻站点最大间隔为d,需要额外大小为d+1的dp数组以及标记数组,所以空间复杂度是。
方法二(哥德巴赫猜想)
1.解题思路
前置知识:
哥德巴赫猜想:任一大于2的偶数,都可表示成两个素数之和。 (引自维基百科)
回到本题中,当相邻间隔不是素数时,如果这个数是偶数,则可以拆成两个素数;如果这个数是奇数(记为n),有两种情况,一种是n-2是素数,那么它可以直接拆分成2和n-2,即拆成2个素数,另一种是n-2不是素数,那么可以拆成1和n-1,而n-1(偶数)一定可以拆成两个素数,即总共拆成3个素数。
动图展示:
2.代码实现
import java.util.*; public class Solution { /** * * @param n int整型 * @param a int整型一维数组 * @return int整型 */ public int work (int n, int[] a) { //初始化结果 int res=n; for(int i=1;i<n;i++){ int d=a[i]-a[i-1]; //如果间隔不是素数,新增对应站点数 if(!isPrime(d)){ res+=segment(d); } } return res; } //判断是否是素数 private boolean isPrime(int x){ //本题中1也当作素数 if(x==1) return true; for(int i=2;i*i<=x;i++){ if(x%i==0){ return false; } } return true; } //哥德巴赫猜想 private int segment(int d){ if(d%2==0){ return 1; } else{ if(isPrime(d-2)){ return 1; } else{ return 2; } } } }
3.复杂度分析
- 时间复杂度:假设相邻站点最大间隔为d,最坏情况下,每次判断素数需要 的时间复杂度,需要判断n次,所以最终的时间复杂度是。
- 空间复杂度:不需要额外的空间,所以空间复杂度是。