dfs_素数环

1. 问题描述:

 输入正整数n,对1-n进行排列,使得相邻两个数之和均为素数,
 输出时从整数1开始,逆时针排列。同一个环应恰好输出一次。
 n<=16

 如输入:6
 输出:
 1 4 3 2 5 6
 1 6 5 2 3 4

2. 我们这里使用dfs来进行解决,尝试走每一条线路,发现这条路径不适合的话退回到上一层,继续搜索它的兄弟,这样便会搜索出每一条可能的路径,在调用dfs之前需要进行剪枝,判断这个数字原来是否使用过,而且没有使用过的这个数字需要和数组的上一个元素加起来之和判断结果是否为素数(使用整形变量k记录记录到数组中存放元素的数量可以知道数组的上一个元素),假如是素数才使用dfs进行搜索。整型变量k记录了当前数组中放了多少个元素,当数组放置的元素的长度等于数组一开始定义的长度,那么此时应该return,而且在return之前判断数组中的首尾元素加起来是否为素数,假如是素数输出数组中的放置的素数环的结果,然后进行return(因为这个环相邻两个数字加起来都是素数)

我们在调用dfs的时候都需要考虑是否需要进行回溯,对于这道题目而言,可回溯也可以不回溯,因为在调用完某个dfs结束之后,那么它仍然在for循环之中,尝试其它可能的元素放入到当前层的元素,所以对当前层的数组没有什么影响,因为新的可能的元素会覆盖掉原来的元素,所以不需要进行回溯也可以,代码片段如下:

for(int i = 2; i <= n; i++){
            if(check(rec, i, k)){
                rec[k] = i;
                dfs(rec, k + 1);
                //不用回溯,因为回到这一层的时候对数组没有什么影响,选择的同一层的其他元素放入到rec[k], k没有变化
            }
 }    

上面可以看出后面放的值会覆盖掉原来的值,而且假如退回到这一层的时候其他的元素都不符合那么退到再上一层发现合适的再dfs再把原来的不符合的数字给覆盖掉,所以回不回溯都无所谓,从中我们知道并不是改变了数组的元素都是需要回溯的,需要看看上一个状态是否对进入到下一个状态有影响,假如没有影响那么可以不用进行回溯

其中需要考虑传入dfs方法中的参数,包括存放元素的数组,记录当前存放到数组中元素的数量,方便对素数环的上一个元素和当前元素之和是否为素数的判断,初始化数组的第一个元素为1,

剩下来的就是一些辅助dfs的方法了,包括判断是否为素数的方法和输出数组元素的方法了

3. 具体的代码如下:

import java.util.Scanner;
public class Main{
    static int n;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        int rec[] = new int[n];
        rec[0] = 1;
        dfs(rec, 1);
    }

    private static void dfs(int[] rec, int k){
        if(k == n){
            if(isPrime(rec[0], rec[n - 1])){
                for(int i = 0; i < n; i++){
                    System.out.print(rec[i] + " ");
                }
                System.out.print("\n");
            }
            return;
        }
        for(int i = 2; i <= n; i++){
            if(check(rec, i, k)){
                rec[k] = i;
                dfs(rec, k + 1);
                //不用回溯,因为回到这一层的时候对数组没有什么影响,选择的同一层的其他元素放入到rec[k],k没有变化

                //假如这一层的元素都不符合它会退到再上一层发现合适的数字dfs后会把原来的元素给覆盖掉
            }
        }    
    }

    private static boolean check(int rec [], int cur, int k) {

        //判断当前元素是否以前以前已经使用
        for(int i = 0; i < k; i++){
            if(rec[i] == cur){
                return false;
            }
        }
        //注意传递的是数组的上一个元素,为rec[k - 1],千万不能够传错
        boolean flag = isPrime(cur, rec[k - 1]);
        if(flag){
            return true;
        }else{
            return false;
        }
    }
    //判断相邻两数之和是否为素数
    private static boolean isPrime(int cur1, int cur2){
        int result = cur1 + cur2; 
        for(int i = 2; i * i <= result; i++){
            if(result % i == 0) return false;
        }
        return true;
    }
}

下面从控制台输入5,模拟其中深搜的过程:

                     1

             2                  

          3              

       4      

     5 

进入dfs(4),发现5不合适那么这一层的dfs调用已经结束,那么退到4这一层循环中的dfs中,此时i = 5但是5不适合继续退回去,退回到3这一层,看一下4是否符合发现4符合不符合继续下一个数字5,发现5符合继续继续dfs,但是发现后面无论如何都不能构成素数环那么结束循环结束退回到2这一层,发现3不行,4可以那么继续进行下去...

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/83447851
今日推荐