链接:https://www.nowcoder.com/questionTerminal/9fededa1b63a41798e5d43344d7bf216?answerType=1&f=discussion
来源:牛客网

有10^8个村庄排在一条公路上,依次编号为010^8-1,相邻村庄距离为1,其中有n个村庄居住着牛牛,居住着牛牛的村庄从小到大依次为a0an-1,其中保证a0=0.
现在需要建设车站,有两个要求必须被满足:
1、每个有牛牛居住的村庄必须修建车站。
2、相邻车站的距离必须为1或为某个质数。
现给出n和a数组,求需要建设车站的最小数量。
示例1
输入
3,[0,7,11]
输出
4
说明
在0,7,8,11处建造车站,差值分别为7,1,3,符合要求

备注:
输入数据包含一个数n和一个长度为n的数组a,数组a的下标为0~n-1,保证a数组递增,且a0=0。
输出一个整数,表示答案。
1<=n<=1000,a数组中数值保证小于10^8

思路

1. 所有有人住的地方必须建站。
2.难点就在于处理差值到底需要多少个素数和1累加得到。如果最暴力的想法可能是直接搜索所有组合并取最小。显然时间是无法接受的。所以这里考虑动态规划来做。不难想到, 当距离差值为n,那么其必定由a + b + ... + c得到, 其中a,b,.....c必定为1或者素数。 所以转移为

dp[n] = min(dp[n], dp[n - k] + 1) 其中k为素数或为1;


3.我们预处理出 距离最大的素数集(这里用的欧拉筛(还是TLE了。 然后对dp数组进行处理,得到距离为1~max所需的最小建站。
4.对初始值sum进行累加即得到答案
5.并没有通过 只是分享思路 结论是真的不知道, 当数据量相对较小时 如 10^6 跑这个算法应该没什么问题.
import java.util.*;


public class Solution {
    public int work (int n, int[] a) {
        // 定义max为相隔两站的最大距离
        int max = Integer.MIN_VALUE;
        for(int i = 1; i < a.length; i++){
            max = Math.max(max, a[i] - a[i - 1]);
        }
        // 欧拉筛 筛出0 ~ 两站最大距离的所有值是否为素数.
        boolean[] check = primeFun(max + 1);
        // 定义sum为总共车站数 其中 居住必须建站初始化为住户数  之后+上距离不为1且不为素数所需要新修的站点数量
        int sum = a.length;
        // dp 预处理两站距离n 最少所需的站点数
        // 因为距离 肯定是由1和素数进行相加得到 所以转移为 dp[n] = min(dp[n], dp[n - k] + 1) 其中k为素数或为1;
        int[] dp = new int[max + 1]; // dp[i]表示 相差距离为i 最小需要修建的车站个数
        for(int i = 1; i <= max; i++){
            // 距离为素数 不需要重新建站
            if(!check[i]){
                dp[i] = 0;
            } else {
                // 距离不为素数 初始化为无穷大并进行转移
                dp[i] = Integer.MAX_VALUE;
                for(int j = 1; j < i; j++){
                    if(!check[j]){
                        dp[i] = Math.min(dp[i], dp[i - j] + 1);
                    }
                }
            }
        }
        for(int i = 1; i < a.length; i++){
            int val = a[i] - a[i - 1];
            // val 两站差值  直接住户数 + 差值最小需要建站数即可
            sum += dp[val];
        }
        return sum;
        // TLE 只是分享下思路.  这结论真不知道
    }

    // 欧拉筛
    private static boolean[] primeFun(int n){
        // check[i] = true 代表是非素数 check[i] = false 代表为素数
        boolean[] check = new boolean[n + 1];
        // 讲道理 这里 check[1]也要为true 但是题意给定 1 也为满足条件 所以设置为false;
        check[0] = true;
        ArrayList<Integer> prime = new ArrayList<>();
        for(int i = 2; i <= n; i++){
            if(!check[i]){
                prime.add(i);
            }
            for(int j = 0; j < prime.size() && i * prime.get(j) <= n; j++){
                check[i * prime.get(j)] = true;
                if(prime.get(j) % i == 0){
                    break;
                }
            }
        }
        return check;
    }
}