首页 > 试题广场 >

N皇后问题

[编程题]N皇后问题
  • 热度指数:2273 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
N皇后问题是指在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行,不同列也不再同一条斜线上,求给一个整数n,返回n皇后的摆法。

输入描述:
输出一个整数,代表n


输出描述:
输出一个整数,代表n皇后的种数。
示例1

输入

1

输出

1
示例2

输入

8

输出

92

备注:
时间复杂度,空间复杂度
直接用传统的深度优先搜索方式,在14皇后的时候会超时,我感觉一般掌握到这个份儿上就差不多了,优化的那个技巧真的太难想了
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        System.out.println(n < 1? 0: solve(0, new int[n], n));
    }
    
    private static int solve(int i, int[] record, int n) {
        if(i == n) return 1;      // 所有列都检查完了,找到了一种合法的摆法
        // 尝试列
        int count = 0;
        for(int j = 0; j < n; j++){
            if(isValid(i, j, record)){
                record[i] = j;
                count += solve(i + 1, record, n);
            }
        }
        return count;
    }
    
    private static boolean isValid(int i, int j, int[] record) {
        for(int k = 0; k < i; k++)
            if(record[k] == j || Math.abs(record[k] - j) == Math.abs(i - k)) return false;    // 共列或共斜线
        return true;
    }
}
这里要想过的话,得用左老师讲过的位运算优化,非常有魔性的算法:
1、对于8皇后问题,先准备一个limit,它是一个右边有8个连续的1,左边都是0的数。
2、在尝试第0行时,能够尝试的位置是  00000000,假如在某一列填了个皇后,此时变为 00001000,那么下一行不能填在这一列,且不能填在左对角线00010000,也不能填在右对角线 00000100,这三个限制合在一起得到总限制00011100。下一行只能在0的位置填皇后,我们利用limit将其转化为11100011,然后逐列进行尝试,每次消掉最右边的1。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        System.out.println(solve((1 << n) - 1, 0, 0, 0));     // 准备一个右边有n个1的数
    }
    
    private static int solve(int limit, int colLim, int leftDiaLim, int rightDiaLim) {
        if(colLim == limit) return 1;      // 所有列都填完了,找到了一种合法的摆法
        int pos = limit & (~(colLim|leftDiaLim|rightDiaLim));      // 当前行可以摆皇后的位置
        int count = 0, mostRightOne = 0;
        while(pos != 0){
            mostRightOne = pos & (~pos + 1);       // 提取最右侧的1,表示从最右边能填的地方开始试
            pos -= mostRightOne;                   // 消掉最右侧试过的位置
            count += solve(limit, colLim | mostRightOne, (leftDiaLim | mostRightOne) << 1, (rightDiaLim | mostRightOne) >>> 1);
        }
        return count;
    }
}
算法的复杂度指标上其实是没有提升的,仍然是O(2n),主要是缩短了常数时间。
编辑于 2021-11-13 15:24:55 回复(0)
思路没有变,递归搜索,时间复杂度还是O(2^n);只是改成用位运算来模拟,减少了空间复杂度至O(1)。

而且位运算比之前快了些,可以通过时间限制,不会又提示“运行超时”了。

import java.util.*;

public class Main {
    
    public static int process(int checkerboard, int current, int leftDiagonal, int rightDiagonal) {
        if (checkerboard == current) {
            // 每一列都放了棋子,放完了
            return 1;
        }
        int res = 0;
        // 找可以放棋子的位置,位上为1可以放置
        // 00 1 0 0 0    leftDiagonal
        // 00 0 0 0 1    rightDiagonal
        // 00 0 0 1 0    current
        // 00 1 0 1 1
        // 11 0 1 0 0
        // 00 1 1 1 1    checkerboard
        // 00 0 1 0 0    pos
        // 00 0 1 0 0    rightmost
        int pos = checkerboard & (~ (leftDiagonal | rightDiagonal | current));
        while (pos != 0) {
            int rightmost = (~pos + 1) & pos;
            pos ^= rightmost;
            // 放棋子,影响后面放置的位置
            res += process(checkerboard, current | rightmost, 
                           (leftDiagonal | rightmost) << 1, (rightDiagonal | rightmost) >>> 1);
        }
        return res;
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        if (n < 1 || n > 32) {
            System.out.println(0);
            return;
        }
        // 位上为1,可以放棋子
        int checkerboard = n == 32 ? -1 : (1 << n) - 1;
        int result = process(checkerboard, 0, 0, 0);
        System.out.println(result);
    }
}
发表于 2020-05-10 15:55:53 回复(0)