n皇后问题解决思路,代码,代码解读以及回溯法的举一反三

1 篇文章 0 订阅
1 篇文章 0 订阅

N皇后问题解决思路

问题:n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击(即同行同列且同一斜边不能出现2个皇后)。

前提:n>3 (否则无解)

思路:已知n<=3无解,那就选最简单的4*4表格

如果第一个格子内放一个皇后那么第一行,第一列,全部都不能再放皇后且与1.1斜边对应的2,2   3,3     4,4都不能放皇后

再2,3再放一个皇后

此时无解在试2,4放一个皇后

显然3,2和4,3在同一斜边上所以第一行放第一列是无解的,所以在第一行第二列上放

第二行只能在2,4的位置放,之后就可以直接看出答案。如下图即

在方格为4的表格里,前一半列数(2)的解为1个那么,水平翻转就是剩下的解,如下图

此时我们的解题思路大概就出来了。

需要一个位置记录黑名单即 摆放过皇后的位置,其同行同列 同一斜边都不能再放。

需要一个记录皇后位置集,

需要一个计数器。

如果我们记录皇后位置的集合用一个N个数字的数组 第一个数字表示第一行第二个数字表示第二行那么。黑名单就可以只记录列和斜边

我们发现一个位置的斜边左上和右下相减是同一个数字,右上和左下相加数值相等,那么我门再用一个列的数组和两个斜边的数组记录黑名单。

贴代码

public class Queen {
    private int[] column; //黑名单1,列已占用
    private int[] rup; //黑名单2,左上右下
    private int[] lup; //黑名单1,
    private int[] queen; //所在对应列的位置
    private int num; //计数器

    public Queen() {
        column = new int[8+1];
        rup = new int[(2*8)+1];
        lup = new int[(2*8)+1];
        for (int i = 1; i <= 8; i++)
            column[i] = 0;
        for (int i = 1; i <= (2*8); i++)
            rup[i] = lup[i] = 0;  //初始定义全部无皇后
        queen = new int[8+1];
    }

    public void backtrack(int i) {
        if (i > 8) {
            showAnswer();
        } else {
            for (int j = 1; j <= 8; j++) {
                if ((column[j] == 0) && (rup[i+j] == 0) && (lup[i-j+8] == 0)) {
                    //若无皇后
                    queen[i] = j; //设定为占用
                    column[j] = rup[i+j] = lup[i-j+8] = 1;
                    backtrack(i+1);  //循环调用
                    column[j] = rup[i+j] = lup[i-j+8] = 0;
                }
            }
        }
    }

    protected void showAnswer() {
        num++;
        System.out.println("\n解答" + num);
        for (int y = 1; y <= 8; y++) {
            for (int x = 1; x <= 8; x++) {
                if(queen[y]==x) {
                    System.out.print("Q");
                } else {
                    System.out.print(".");
                }
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Queen queen = new Queen();
        queen.backtrack(1);
    }
}

代码解读

如上所说,定义一个列,一个坐上右下(相减是同一个数)和右上左下(相加是同一个数)的黑名单

代码开头初始化几个数组的值,8皇后问题,数组长度都+1是为了后面从1开始,下面的循环都是从1开始,所以这个不必纠结。

然后开始执行主体,主体其实是一个递归,主体后面讲,我们先看出口1,出口1里面是一个解答答案的过程其实就是连个for循环循环输出而已,这里其实已经是答案弄好了。这里也很简单。

但是答案怎么来,看条件,每次i>8说明什么?i是什么?

i可以理解为行数,i大于8说明递归的时候i在第八行 有合适的位置让皇后放,也就是已经有一种解了 。

 

出口2,是for循环里面1-8的位置都不符合放皇后的要求。循环直接循环完。这一种方案是无解的,说明上一层位置不能选这个位置,所以上一层的黑名单位置去除掉,

(理论上这里也应该将queen[i]=0将,queen数组i的位置上置空。但是后面也会重复工作也会重复赋值,所以可以省去这个工作。)

此时的递归的方法走完,跳出来,执行上一层的黑名单置空。然后在上一层下一个位置j+1在进行判断

看递归上两行代码,

i行如果第j个列满足条件可以放皇后条件即使这个坐标的列没有被加入黑名单,坐上右下的黑名单和右上左下的黑名单也没有它。(条件:

这个递归在for循环里,其实实际上就是两个for循环循环套用。

温故而知新

这个问题是用回溯法解决的(穷举法暴力破解)。举一反三,以后遇到需要用回溯法解决问题的时候,代码就可以写成这样。用递归解决。递归之前写占位操作(以后进行判断)递归之后写清除操作

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值