面试题:八皇后问题(N皇后问题)

前言

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?这道题目也可以稍微延伸一下,变为 N×N的棋盘上放置N个皇后,其他条件相同。
下面介绍一种比较简单易懂的实现方式。

项目下载地址

正文

算法

先说一下算法, 这里使用的是一个改良版的广度优先搜索算法。在N×N的棋盘上,我们先在第一行的第一个位置放置下皇后,接着我们就不去管第一行了,因为第一行已经不能放置皇后了。我们在第二行找到所有的可以放置皇后的位置。同理我们现在可以不用去管前两行了。我们对于第二行的每一个可以放置皇后的位置,都在第三行继续寻找可以放置皇后的位置,如此往复,直到我们遍历到最后一行。这个时候我们就得到了一部分解,这些解是对于第一个皇后放置在第一行第一列的位置而言。接下来对于第一行第二列、第三列…所有列都进行这个步骤,就得到了所有的解。

代码

为了更加直观,我们模拟出一个N×N的棋盘。我们把每次放置一个皇后之后的局面称为一个状态(State)。下面是State类的代码:

import java.util.ArrayList;
import java.util.List;

public class State {
    private List<Point> pointList = new ArrayList<Point>();
    private int lineNum;

    public List<Point> getPointList() {
        return pointList;
    }

    public int getLineNum(){
        return lineNum;
    }

    public void setLineNum(int lineNum){
        this.lineNum = lineNum;
    }

}

每个state对象有两个属性,pointList存放的是当前的state下已经放置的皇后坐标,lineNum是当前state所遍历到的行数。其中用到的Point类代码如下:

public class Point{
    private int X;
    private int Y;

    public Point(int x, int y){
        this.X = x;
        this.Y = y;
    }

    public int getX(){
        return this.X;
    }

    public int getY(){
        return this.Y;
    }

    public void setX(int x){
        this.X = x;
    }

    public void setY(int y){
        this.Y = y;
    }

}

每个Point对象有一个X坐标和一个Y坐标。
下面是主程序:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class EightQueen {
    //起始状态列表
    public static List<State> startStates = new ArrayList<State>();

    //棋盘的行列数和要放置的皇后数量
    public static final int lineNum = 4;

    //一个N×N的棋盘
    public static Point[][] allPoints = new Point[lineNum][lineNum];

    //解法数量
    public static int count = 0;

    public static void main(String[] args) {

        //初始化棋盘
        for(int i=0; i<lineNum; i++){
            for(int j=0; j<lineNum; j++){
                allPoints[i][j] = new Point(i, j);
            }
        }

        //初始化起始状态列表。每个State的PointList分别存放了第一行的8个坐标,并且设置第一行为遍历初始行
        for(int i=0; i<lineNum; i++){
            State state = new State();
            state.getPointList().add(new Point(0, i));
            state.setLineNum(0);
            startStates.add(state);
        }

        //对于初始化state列表中的每个state,进行遍历操作。
        for(State state : startStates){
            calculate(state);
        }
        System.out.println("总数为:" + count);
    }

    public static void calculate(State state)
    {
        Stack<State> stack = new Stack<State>();
        stack.push(state);
        while(!stack.isEmpty()){
            //从stack里取出一个状态
            State state2 = stack.pop();
            //如果已经遍历到最后一行,输出这个解
            if(state2.getLineNum() == lineNum - 1){
                for(Point goalpoint : state2.getPointList()){
                    for(int i=0; i<lineNum; i++){
                        if(i!=goalpoint.getY())
                            System.out.print("_ ");
                        else
                            System.out.print("Q ");
                    }
                    System.out.println();
                }
                System.out.println();
                count++;
                continue;
            }

            //否则寻找下一行可以放置皇后的位置
            int currentLineNum = state2.getLineNum() + 1;
            for(Point point : allPoints[currentLineNum]){
                //如果该点可以放置皇后
                if(isSatisfied(point, state2.getPointList()))
                {
                    //创建一个state对象
                    State newState = new State();
                    //把这个新的state的pointList设置为前一个点的pointList里的所有点加上当前的点的坐标
                    for(Point point2 : state2.getPointList()){
                        newState.getPointList().add(new Point(point2.getX(), point2.getY()));
                    }
                    newState.getPointList().add(point);
                    //设置新的state的行数为下一行
                    newState.setLineNum(currentLineNum);
                    //入栈
                    stack.push(newState);
                }
            }
        }
    }

    //判断一个点是否可以放置皇后
    public static boolean isSatisfied(Point point, List<Point> list){
        for(Point point2 : list){
            //两个皇后不能再同一条横线、直线、斜线上。由于我们直接遍历的是下一行的点,所以肯定不会出现X坐标相同的情况
            if(point2.getY() == point.getY()
                    || Math.abs(point2.getX() - point.getX()) == Math.abs(point2.getY() - point.getY()))
                return false;
        }
        return true;
    }

}

程序的输出为

_ Q _ _
_ _ _ Q
Q _ _ _
_ _ Q _ 

_ _ Q _
Q _ _ _
_ _ _ Q
_ Q _ _ 

总数为:2

如果我们更改lineNum为6,输出为

_ Q _ _ _ _
_ _ _ Q _ _
_ _ _ _ _ Q
Q _ _ _ _ _
_ _ Q _ _ _
_ _ _ _ Q _ 

_ _ Q _ _ _
_ _ _ _ _ Q
_ Q _ _ _ _
_ _ _ _ Q _
Q _ _ _ _ _
_ _ _ Q _ _ 

_ _ _ Q _ _
Q _ _ _ _ _
_ _ _ _ Q _
_ Q _ _ _ _
_ _ _ _ _ Q
_ _ Q _ _ _ 

_ _ _ _ Q _
_ _ Q _ _ _
Q _ _ _ _ _
_ _ _ _ _ Q
_ _ _ Q _ _
_ Q _ _ _ _ 

总数为:4

由于lineNum = 8的时候输出太长,这里不做展示。结果的数量为92种。
这里附上不同lineNum对应的解法数量:

lineNum     solution(lineNum)
1           1
2           0
3           0
4           2
5           10
6           4
7           40
8           92
9           352
10          724
11          2680
12          14200
13          73712
14          365596
15          2279184
16          14772512
17          95815104
18          666090624
19          4968057848
20          39029188884
21          314666222712
22          2691008701644
23          24233937684440
24          227514171973736
25          2207893435808352  
时间: 2024-09-08 23:02:22

面试题:八皇后问题(N皇后问题)的相关文章

UVa 639:Don&#039;t Get Rooked, 类八皇后问题

题目链接: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=108&page=show_problem&problem=580 题目类型: 暴力, 回溯法 题目: In chess, the rook is a piece that can move any number of squares vertically or horizontally. In this p

皇后控制问题 acm问题问下

问题描述 皇后控制问题 acm问题问下 皇后控制问题 查看 提交 统计 提问 总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 128000kB 描述 在一个n×n个方格组成的棋盘上的任一方格中放置一个皇后,该皇后可以控制他所在的行,列以及对角线上的所有方格.对于给定的自然数n,在n×n个方格组成的棋盘上最少要放置多少个皇后才能控制棋盘上的所有方格,且放置的皇后互不攻击? 编程任务:设计一个算法,对于给定的自然数n (1≤n≤100)计算在n×n个方格组成的棋盘上最少

[程序员面试题精选100题]58.八皇后问题

题目 在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行.同一列或者同一对角斜线上.下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法.请求出总共有多少种摆法. 思路 这就是有名的八皇后问题.解决这个问题通常需要用递归,而递归对编程能力的要求比较高.因此有不少面试官青睐这个题目,用来考察应聘者的分析复杂问题的能力以及编程的能力. 由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行.于是我们可以定义一个数组colIndex[8],colI

《爱丽丝》明日公映美工特效广州试片场获好评

<爱丽丝梦游仙境>剧照<爱丽丝梦游仙境>剧照<爱丽丝梦游仙境>剧照 ●顺应潮流的3D视效,拥有强大受众基础的原著小说,<爱丽丝梦游仙境>正在全球写下票房佳绩. ●本报鉴定:后期转制的3D画面立体感略嫌不足,部分情节也略显老套.但如果你有"爱丽丝"情结或是蒂姆波顿的粉丝,你会喜欢它的. 本报讯 (见习记者 简芳)鬼才导演蒂姆波顿的3D新作<爱丽丝梦游仙境>(AliceinWonder-land)终于可以在本周五登陆内地银幕了!那

3D大片《爱丽丝梦游仙境》月底内地上映(图)

<爱丽丝梦游仙境>海报 根据1865年刘易斯卡罗尔经典童话故事<爱丽丝漫游奇境记>而改编的3D电影<爱丽丝梦游仙境>,将于本月26日在全国上映.影片早在3月初就已在北美公映,连续两周拿下了当周票房冠军,累积票房2.6亿美元.而在邻近广东的香港,上映两周后票房也接近4000万港元,全面终结<阿凡达>此前的票房霸主地位.记者前日在香港观看了3D版的<爱丽丝梦游仙境>,并采访了迪士尼亚洲区市场及推广宣传总监卢先生,对方表示<爱丽丝梦游仙境>

八皇后问题的C#解答

解答|问题 改编自V星[视窗王子]应答程序,如下:<br><br>using System;<br>class Queen{<br>    const int SIZE = 8;//皇后数<br>    public static void Main()<br>    {<br>        int[] Queen = new int [SIZE];//每行皇后的位置<br>        int y,x,i

UVa 167:The Sultan&#039;s Successors, 八皇后问题

题目链接: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=108&page=show_problem&problem=103 题目类型: 回溯 原题: The Sultan of Nubia has no children, so she has decided that the country will be split into up to k separate

基于Delphi的八皇后问题动态实现

摘要 对于八皇后问题的实现,如果结合动态的图形演示,则可以使算法的描述更形象.更生动,使教学能产生良好的效果. 关键词 八皇后问题 冲突 数据结构 线程类 八皇后问题是一个古老而著名的问题,是回溯算法的典型例题.该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 下面用delphi6实现的八皇后问题的动态图形程序,能够演示全部的92组解.八皇后问题动态图形的实现,主要应解决以下

常用算法:C#八皇后问题

八皇后问题是一个古老而著名的问题,是回溯算法的典型应用.八皇后问题就是棋盘上的8个皇后不能在同一行.一列或一条斜线上,在8!=40320种排列中共有92种解决方案.代码如下: using System; using System.Collections.Generic; using System.Text;namespace ExQueen { class Queen { public void QueenArithmetic(int size){ int[] Queen = new int[s