杭电ACM OJ 1016 Prime Ring Problem 回溯法+ 高效判断素数 快速轻松解决

简单易理解的回溯法,高效判断素数,可以看我另外两篇博客,这里我也快速简要讲一下。


这一题主要是要领悟回溯法,很关键。

先写一个正常的n个数的求各种排列情况。就1到。。n。的各种排列情况。

方法:正常思路是第一个位置有n种情况,你要写一个长度n的for循环,第二个位置自然是n-1种情况,所以要写一个长度n的for循环,以此类推。。。但是问题来了,你怎么知道传进来的n是多少?你又要写几个for循环?所以你需要用回溯法来递归处理这个情况(当然也有不用递归的方法)。你需要写这样一个方法:你需要写一个标记数组,用来标记这个数字是否访问过了。还有一个标记当前正在处理哪个位数的level,如果level是0,那就是正在为第一位找,所以说不管从哪个位置开始找,方法都是一样的,唯一不同的是你正在为哪一位找以及有哪些数组已经用过了。

上代码

private void sort(int level) {
    for (int i = 0; i < n; i ++) {
        if (!flag[i]) {
            current[level] = i + 1;
            flag[i] = true;//比如倒数第二层:锁一个
            if (level != n - 1) {
                sort(level + 1);//留一个给最后一层
            } else {
                System.out.println(Arrays.toString(current));
            }
            flag[i] = false;//最后一层搞完后,恢复这一层,让剩下那个数再搞
        }
    }
}
当你在最后一层的时候,就只能找到一个数了,就返回上一个循环,上一个循环可以找到两个数,第一个数已经找到符合的情况了,再用第二个数。再上一层也是一,以此类推。那么我们是怎么样实现这个回溯的功能的呢?其实for循环和递归写在那里了,他本身就可以回溯回去。我们唯一要做的就是,开始进入下一个循环之前,把当前的数标记为已经访问,完全退出下一个循环后,再把当前的数标记为未访问状态,当前的循环再去找下一个数。

讲的有点长,还是得去看看我原来的文章,讲的又简单又清楚。

[1, 2, 3, 4]
[1, 2, 4, 3]
[1, 3, 2, 4]
[1, 3, 4, 2]
[1, 4, 2, 3]
[1, 4, 3, 2]
[2, 1, 3, 4]
[2, 1, 4, 3]
[2, 3, 1, 4]
[2, 3, 4, 1]
[2, 4, 1, 3]
[2, 4, 3, 1]

。。。

可以看到已经成功输出了,搞定了回溯法后就可以轻松解决这个问题了,只需要在代码里改动小小的地方即可。

针对这题,就是:1.修改一下一个if语句,

if (!flag[i] && isPrime(current[level - 1] + i + 1)) {
与上一个数之和为素数

2.1不用放入回溯法内


public class PrimeRingProblem1016 {

    private int n;
    private int[] current;
    private boolean[] flag;

    PrimeRingProblem1016(int n) {

        this.n = n;

        current = new int[n];
        flag = new boolean[n];

        current[0] = 1;
    }

    //高效判断素数
    private boolean isPrime(int num) {
        if (num == 2 || num == 3) {
            return true;
        }

        //如果不在6的倍数附近,肯定不是素数
        if (num % 6 != 1 && num % 6 != 5) {
            return false;
        }
        //6倍数附近的数进行判断

        //这里添加了一个num % (i + 2) == 0,为什么?
        //枚举一下i,i+2可能的取值,因为i +=6
        //所以是这样的 5 7,11 13,17 19
        //为什么呢?举一个35=36-1)的情况,是57的倍数
        //再举一个 49=48+1),是7的倍数
        //所以知道了,6附近的两个数不一定是质数,有可能是57的倍数
        for (int i = 5; i <= Math.sqrt(num); i += 6) {
            if (num % i == 0 || num % (i + 2) == 0) {
                return false;
            }
        }
        return true;
    }

    private void sort(int level) {

        for (int i = 1; i < n; i++) {
            if (!flag[i] && isPrime(current[level - 1] + i + 1)) {
                current[level] = i + 1;
                flag[i] = true;
                if (level != n - 1) {
                    sort(level + 1);
                } else {
                    System.out.println(Arrays.toString(current));
                }
                flag[i] = false;
            }
        }

    }

    public static void main(final String[] args) throws Exception {

        PrimeRingProblem1016 primeRingProblem1016 = new PrimeRingProblem1016(6);
        primeRingProblem1016.sort(1);

    }
}

我好想忘了1和最后一个数判断了,不过,没关系吧。。。

你可能感兴趣的:(算法题)