使用回溯法和排列树(降维和减枝)解决N皇后问题

上一篇博客使用回溯法和子集树(降维法)解决N皇后问题
运用了降维,极大的提高了计算效率。不过能不能再精益求精呢?~能的,还能继续优化。前面运用了排列树,对于4x4来说,解空间树规模为nn = 44 = 256。这里我们采用减枝进一步优化。
思路很简单,假如x[0] = 1,则x[1],x[2],x[3]就不可能在等于1。
x[0] = 1就代表皇后摆放在第一行的第2个位置。则后面2,3,4行就不可能摆放在第二个位置。
这里就有点像对0123进行排列组合,有1023、1032等等,然后一个一个验证是否符合摆放要求。
所以由原来的子集树转换为了排列树。
子集规模树(排列树),一共有4层,解空间树规模(最底层)为n! = 4! = 24。
使用回溯法和排列树(降维和减枝)解决N皇后问题_第1张图片

代码实现


/**
 * 回溯 & 降维(一维) & 排列树
 */
public class NQueens3 {

    /**
     * 皇后个数
     */
    private int n;

    /**
     * 当前解
     */
    private int[] currentSolution;

    /**
     * 当前已经找到可行的方案个数
     */
    private long sum;

    public NQueens3(int n) {
        this.n = n;
        this.sum = 0;
        this.currentSolution = new int[n];
        // 这里要初始,避相同
        for(int i = 0; i < n; i++) {
            currentSolution[i] = i;
        }
        backtrack(0);
    }

    public boolean isSuitablePlacement(int t) {
        // 因为是对称的,所以第一行只需要计算一半即可
        if(t == 0) {
            if((n + 1) / 2 <= currentSolution[t]) {
                return false;
            }
        }
        for(int i = 0; i < t; i++) {
            // 判断列
            if(currentSolution[i] == currentSolution[t]) {
                return false;
            }
            // 判断斜向,这里通过斜率判断
            if(Math.abs(currentSolution[i] - currentSolution[t]) == t - i) {
                return false;
            }
        }
        return true;
    }

    public void saveResult() {
        // 这里可以打印放置情况
    }

    public void swap(int i, int j) {
        int temp = currentSolution[i];
        currentSolution[i] = currentSolution[j];
        currentSolution[j] = temp;
    }

    public void backtrack(int t) {
        if(t >= n) {
            sum++;
            saveResult();
            return;
        }
        for(int i = t; i < n; i++) {
            swap(i, t);
            // 判断摆放是否合适,不合适就回溯
            if(isSuitablePlacement(t)) {
                backtrack(t + 1);
            }
            // 还原位置
            swap(i, t);
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        NQueens3 nQueens = new NQueens3(14);
        System.out.println(nQueens.sum);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

12皇后
使用子集树所需时间(单位毫秒):194
使用排列树(剪枝)所需时间(单位毫秒):119
13皇后
使用子集树所需时间(单位毫秒):1113
使用排列树(剪枝)所需时间(单位毫秒):768
14皇后
使用子集树所需时间(单位毫秒):5918
使用排列树(剪枝)所需时间(单位毫秒):3890

你可能感兴趣的:(数据结构与算法)