青训营X豆包MarsCode 技术训练AI刷题小Q的非素数和排列问题 | 豆包MarsCode AI 刷题

81 阅读3分钟

问题描述

小C对排列很感兴趣,她想知道有多少个长度为n的排列满足任意两个相邻元素之和都不是素数。排列定义为一个长度为n的数组,其中包含从1到n的所有整数,每个数字恰好出现一次。


测试样例

样例1:

输入:n = 5
输出:4

样例2:

输入:n = 3
输出:0

样例3:

输入:n = 6
输出:24

解题思路

数据结构选择

  1. 排列的存储:我们可以使用 List<Integer> 来存储每个排列。
  2. 所有排列的存储:使用 List<List<Integer>> 来存储所有生成的排列。

算法步骤

  1. 生成所有排列

    • 使用回溯法生成所有可能的排列。
    • 递归地选择每个数字,并确保每个数字只出现一次。
  2. 检查排列是否满足条件

    • 遍历每个排列,检查相邻元素之和是否为素数。
    • 如果所有相邻元素之和都不是素数,则该排列满足条件。
  3. 统计满足条件的排列数量

    • 遍历所有生成的排列,统计满足条件的排列数量。

具体步骤

  1. 生成排列

    • 使用回溯法生成所有排列。
    • 递归地选择每个数字,并确保每个数字只出现一次。
  2. 检查排列

    • 遍历每个排列,检查相邻元素之和是否为素数。
    • 如果所有相邻元素之和都不是素数,则该排列满足条件。
  3. 统计结果

    • 统计满足条件的排列数量。

代码

import java.util.*;
public class Main {
    public static int solution(int n) {
        // 生成所有排列
        List<List<Integer>> permutations = generatePermutations(n);
        
        int count = 0;
        for (List<Integer> perm : permutations) {
            if (isValidPermutation(perm)) {
                count++;
            }
        }
        
        return count;
    }

    // 生成所有排列
    private static List<List<Integer>> generatePermutations(int n) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> nums = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            nums.add(i);
        }
        backtrack(result, new ArrayList<>(), nums);
        return result;
    }

    // 回溯生成排列
    private static void backtrack(List<List<Integer>> result, List<Integer> tempList, List<Integer> nums) {
        if (tempList.size() == nums.size()) {
            result.add(new ArrayList<>(tempList));
        } else {
            for (int i = 0; i < nums.size(); i++) {
                if (tempList.contains(nums.get(i))) continue; // 元素已经在当前排列中
                tempList.add(nums.get(i));
                backtrack(result, tempList, nums);
                tempList.remove(tempList.size() - 1);
            }
        }
    }

    // 检查排列是否满足条件
    private static boolean isValidPermutation(List<Integer> perm) {
        for (int i = 0; i < perm.size() - 1; i++) {
            if (isPrime(perm.get(i) + perm.get(i + 1))) {
                return false;
            }
        }
        return true;
    }

    // 判断一个数是否为素数
    private static boolean isPrime(int num) {
        if (num <= 1) return false;
        for (int i = 2; i <= Math.sqrt(num); i++) {
            if (num % i == 0) return false;
        }
        return true;
    }

    public static void main(String[] args) {
        System.out.println(solution(5) == 4);
        System.out.println(solution(3) == 0);
        System.out.println(solution(6) == 24);
    }
}

优化思路

  1. 减少重复计算

    • 素数判断是一个耗时的操作,可以考虑预先计算并存储素数信息,避免在每次检查排列时重复计算。
  2. 剪枝

    • 在生成排列的过程中,如果发现当前部分排列已经不满足条件,可以提前终止递归,减少不必要的计算。
  3. 优化排列生成

    • 使用更高效的排列生成算法,例如使用 Collections.shuffle 来生成随机排列,并检查其是否满足条件。
  4. 并行计算

    • 对于较大的 n,可以考虑使用多线程并行计算,加快排列的生成和检查过程。

具体优化方向

  1. 预计算素数

    • 预先计算并存储所有可能的和是否为素数的信息。
  2. 剪枝优化

    • 在生成排列的过程中,如果发现当前部分排列已经不满足条件,提前终止递归。
  3. 优化排列生成

    • 使用 Collections.shuffle 生成随机排列,并检查其是否满足条件。
  4. 并行计算

    • 使用多线程并行计算,加快排列的生成和检查过程。