八皇后问题求解

我今天好不容易上了一次课,然后数据结构老师给我们留大作业,丧心病狂,先解决一个叫八皇后的问题。

题目背景:

【问题描述】

在一个8×8的棋盘里放置8个皇后,要求每个皇后两两之间不相"冲"(在每一横列竖列斜列只有一个皇后)。

【问题分析】

数组a、b、c分别用来标记冲突,a数组代表列冲突,从a[0]~a[7]代表第0列到第7列,如果某列上已经有皇后,则为1,否则为0;

数组b代表主对角线冲突,为b[i-j+7],即从b[0]~b[14],如果某条主对角线上已经有皇后,则为1,否则为0;

数组c代表从对角线冲突,为c[i+j],即从c[0]~c[14],如果某条从对角线上已经有皇后,则为1,否则为0;

【基本要求】

[if !supportLists](1) [endif]用递归算法实现

[if !supportLists](2) [endif]输出所有棋盘状态,其中空的地方为“*”,放置皇后的地方为“@”

11.最小生成树求解

【问题描述】

在n个城市之间建设网络,只需保证连通即可,求最经济的架设方法。

【基本要求】

(1)假设已知n个城市之间的网络是一个带权连通无向图。

(2)用邻接矩阵COST表示给定的带权连通无向图。知阵元素定义为

(3)利用普里姆或克鲁斯卡尔算法求图的最小生成树。

然后我们现在工具问题的描述进行分析:

首先,可归纳问题的条件为,8皇后之间需满足:

             1.不在同一行上

             2.不在同一列上

             3.不在同一斜线上

             4.不在同一反斜线上

     这为我们提供一种遍历的思路,我们可以逐行或者逐列来进行可行摆放方案的遍历,每一行(或列)遍历出一个符合条件的位置,接着就到下一行或列遍历下一个棋子的合适位置,这种遍历思路可以保证我们遍历过程中有一个条件是绝对符合的——就是下一个棋子的摆放位置与前面的棋子不在同一行(或列)。接下来,我们只要判断当前位置是否还符合其他条件,如果符合,就遍历下一行(或列)所有位置,看看是否继续有符合条件的位置,以此类推,如果某一个行(或列)的所有位置都不合适,就返回上一行(或列)继续该行(或列)的其他位置遍历,当我们顺利遍历到最后一行(或列),且有符合条件的位置时,就是一个可行的8皇后摆放方案,累加一次八皇后可行方案的个数,然后继续遍历该行其他位置是否有合适的,如果没有,则返回上一行,遍历该行其他位置,依此下去。这样一个过程下来,我们就可以得出所有符合条件的8皇后摆放方案了。这是一个深度优先遍历的过程,同时也是经典的递归思路。

      接下来,我们以逐列遍历,具体到代码,进一步说明。

        首先,从第一列开始找第一颗棋子的合适位置,我们知道,此时第一列的任何一个位置都是合适的,当棋子找到第一个合适的位置后,就开始到下一列考虑下一个合适的位置,此时,第二列的第一行及第二行显然就不能放第二颗棋子了,因为其与第一个棋子一个同在一行,第二列第二行同在一条斜线上。第二列第三行成为第二列第一个合适的位置,以此类推,第三列的第5行又会是一个合适位置,这个过程中,我们注意到,每一列的合适位置都是受到前面几列的位置所影响,归纳如下:

      假设前面1列的棋子放在第3行,那当前列不能放的位置就一定是3行,2行,4行。因为如果放在这三行上就分别跟前一列的棋子同在一行、同在斜线、同在反斜线上,不符合我们的要求。现在我们用cols数组来表示8个列棋子所放的行数,数组下标从0开始,其中数组下标表示列数,数组的元素值表示该列棋子所在行数,当前列为N(N>=0,N<8),即cols[N-1]=3,则有:

                      cols[N] != cols[N-1](=3,表示不在同一行)

                      cols[N] != cols[N-1]-1(=3-1=2,表示不在同一斜线上)

                      cols[N]!=cols[N-1]+1(=3+1,表示不在同一反斜线上)

      这里我们注意到,如果N-2列存在的话,那么我们还要考虑当前列N不与N-2列的棋子同行,同斜线,同反斜线。把当前列N的前面的某一列设为m,则m的所有取值为{m>=0,m

                    cols[N] != cols[m](与第m列的棋子不在同一行)

                    cols[N] != cols­­[m] -(N-m)(>=0 ,与第m列的棋子不在同一斜线上)

                    cols[N] != cols­­[m] + (N-m)  (<=8-1,与第m列的棋子不在同一反斜线上)          

           具体到代码,很显然,取m的所有值只需要一句循环,同时我们为每一列定义一个长度为8的布尔数组row[],下标同样是从0开始,我们规定当row[i]=true时,表示该列第i行不能放棋子。这样我们就能写成下列程序段了:


八皇后问题求解_第1张图片
棋表图


八皇后问题求解_第2张图片
标记

增加一点:你其实也可以这样子想,我某一行和下面一行的横坐标数字相差不能超出集合下标的差。因为你这样子想,我两行最多相差16,因为每次都是先遍历第一行后再遍历第二行,所以不能超出这个范围。

第二点是:不能出现重复元素。重复了还玩个球球。就是集合表示的元素。

public static int num =0; //累计方案总数

public static final int MAXQUEEN =8;//皇后个数,同时也是棋盘行列总数

public static int[]cols =new int[MAXQUEEN]; //定义cols数组,表示8列棋子摆放情况

public Queen8() {//核心函数

    getArrangement(0);

    System.out.print("/n");

    System.out.println(MAXQUEEN+"皇后问题有"+num+"种摆放方法。");

}

public void  getArrangement(int n) {

//遍历该列所有不合法的行,并用rows数组记录,不合法即rows[i]=true

    boolean[] rows =new boolean[MAXQUEEN];

    for (int i =0; i < n; i++) {

rows[cols[i]] =true;

        int d = n - i;

        if (cols[i] - d >=0) rows[cols[i] - d] =true;

        if (cols[i] + d <=MAXQUEEN -1) rows[cols[i] + d] =true;

    }

for (int i =0; i

//判断该行是否合法

        if (rows[i])continue;

        //设置当前列合法棋子所在行数

        cols[n] = i;

        //当前列不为最后一列时

        if (n

getArrangement(n +1);

        }else {//累计方案个数

            num++;

            //打印棋盘信息

            printChessBoard();

        }

}

}

public void printChessBoard(){

System.out.print("\n"+"第"+num+"种走法 "+"\n");

    for(int i=0;i

for(int j=0;j

if(i==cols[j]){

System.out.print("0 ");

            }else

                System.out.print("+ ");

        }

System.out.print("\n");

    }

}

public static void main(String args[]){

Queen8 queen =new Queen8();

    queen.printChessBoard();

}

        思路嘛其实很简单,就是为了避免两个棋子有相互接触的空间,然后先遍历第一行,然后得到位置以后放到数组里面,然后遍历第二行,获取位置放到数组,然后进行比对,看是否合适,合适继续遍历,不合适就重新返回第一行重新规划位置,继续遍历。

        这种题目属于深度优先遍历的算法,当然也很无聊。就是平常下棋的时候的用的招数,只是平常没有想到还可以做算法,不过作为锻炼思维,倒是一种很好的玩具。这个算法可以运用到递归,不断判断然后return,继续遍历。

OK,介绍完毕。

你可能感兴趣的:(八皇后问题求解)