ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2020/10/29N皇后

2020-10-30 20:01:50  阅读:242  来源: 互联网

标签:29N 10 int void cols ways place 皇后 2020


目录

2020/10/29N皇后

本周是跟着B站教学视频学习回溯+剪枝。

八皇后问题

八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。

问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

先放代码

public class Queen {
    public static void main(String[] args) {
        new Queen().placeQueens(8);
    }
     int[] cols;
     int ways = 0;
     void placeQueens(int n){
        if(n < 1)
            return;
        cols = new int[n];
        place(0);
        System.out.println(n+"皇后一定有"+ways+"种摆法");
    }
    void place(int row){
         if(row == cols.length){
             ways++;
             show();
             return;
         }
      for(int col = 0; col<cols.length;col++){
          if(isVaild(row,col)){
                cols[row] = col;
                place(row+1);//执行不下去时则会回溯,重点在于对递归的一个理解
          }
      }
    }
    boolean isVaild(int row, int col){
         for(int i = 0;i < row;i++){//这里用了一个for循环,每次需要取出每一行的cols来对比是否已经放置皇后
             if(cols[i] == col)
                 return false;
             if(row -i == Math.abs(col-cols[i]))//这里涉及到了初中数学的k= 1 || -1来判定斜线
                 return false;

         }
         return true;
    }
    void show(){
         for(int row = 0; row < cols.length;row++){
             for(int col = 0; col < cols.length;col++){
                 if(cols[row] == col){
                     System.out.print("1  ");
                 }else {
                     System.out.print("0  ");
                 }
             }
             System.out.println();
         }
        System.out.println("---------------------------");
    }
}

先来简单讲讲对这个回溯的理解,其实就是对递归的一个应用,对于递归最容易理解的应该还是一颗二叉树,想象一下迷宫里,一个人走到岔路口,有两个位置可以走,走左边的人和走右边的人将产生两种不同的结果,并且不断地需要去做出选择,这一次是选择走左边还是走右边,在没有走到尽头(死胡同)时,这个人将一直走下去。

这里呢使用了cols数组来存放每一行皇后的摆放位置,再接着调用自身去计算下一行的皇后位置,如果在当前for循环中,isVaild()函数的判断结果都是false,那么就会接着回溯上一行。

但不知道大家会不会有和我同样的想法呀,就是这个cols数组装的东西,不会被反复覆盖掉最后根本就没法正常实现记录功能嘛,打印出来才发觉自己糊涂了,明显也就是对于每一row,都将重新复制(覆盖掉),而这个递归的执行顺序时线性的,也就是选择走左边的人将走到尽头(死胡同)时,它将自己传送到上一次做出决策的地方,也就是上一个路口的另外一个方向。

image-20201029211318916

看下图应该对程序的执行顺序更为直观,重点关注place(1),place(2),place(1)这几个变动时发生了什么。

image-20201029213745560

image-20201029214833782

也就是在这个for循环中,在第一个if判断语句成立时,它进入下一个place(),当place()走投无路,它会回到原本的for循环中其他成立的isValid()下继续进行其他的递归。

优化一:对剪枝进行优化

查看原本的代码,发现isVaild()的判断是用了一个for循环来进行,于是这一个块可以以空间换时间,节约至O(1)的复杂度。

但是效率虽然提高,但是会发现,我们没有办法去追踪这个八皇后是被摆在了什么位置,因为cols每次都被覆盖掉,解决方案是增加一个数组去存储(但这里就偷懒不写啦)。

public class Queen2 {
    public static void main(String[] args) {
        new Queen2().placeQueens(8);
    }
     //int[] cols;
     int ways = 0;
     boolean[] cols;
     boolean[] leftTop;//左上角到右下角的斜线
     boolean[] rightTop;//右上角到左下角的斜线
     void placeQueens(int n){
        if(n < 1)
            return;
        cols = new boolean[n];
        leftTop = new boolean[(n<<1) -1];//2*n n左移一位
        rightTop = new boolean[leftTop.length];//此时已经不用再去做一次计算,尽量优化
        place(0);
        System.out.println(n+"皇后一定有"+ways+"种摆法");
    }
    void place(int row){
         if(row == cols.length){
             ways++;
             show();
             return;
         }
      for(int col = 0; col<cols.length;col++){
              if(cols[col])
                  continue;
              int ltIndex =row-col+cols.length-1;
              int rtIndex = row+col;
              if(leftTop[ltIndex])
                  continue;
              if(rightTop[rtIndex])
                  continue;
              cols[col] = true;
              leftTop[ltIndex] = true;
              rightTop[rtIndex] = true;
//                cols[row] = col;
                place(row+1);
          cols[col] = false;//还原代码
          leftTop[ltIndex] = false;
          rightTop[rtIndex] = false;

      }
    }
//    boolean isVaild(int row, int col){
////         for(int i = 0;i < row;i++){
////             if(cols[i] == col)
////                 return false;
////             if(row -i == Math.abs(col-cols[i]))
////                 return false;
////
////         }
//         return true;
//
//    }
    void show(){
//         for(int row = 0; row < cols.length;row++){
//             for(int col = 0; col < cols.length;col++){
//                 if(cols[row] == col){
//                     System.out.print("1  ");
//                 }else {
//                     System.out.print("0  ");
//                 }
//             }
//             System.out.println();
//         }
//        System.out.println("---------------------------");
    }
}

优化二:用位运算降低空间复杂度

这一点的思路在于布尔数组存的是true/fasle,true-》1,false-》0,在皇后的情形下,一个数组可以替换成一个字节,如00100111 即byte cols;,两个字节则是使用short rightTop来替代

public class Queen3 {
    public static void main(String[] args) {
        new Queen3().placeQueens();
    }
    int ways = 0;
    byte cols;
    short leftTop;//左上角到右下角的斜线
    short rightTop;//右上角到左下角的斜线

    void placeQueens() {
        place(0);
        System.out.println(8 + "皇后一定有" + ways + "种摆法");
    }
    void place(int row) {
        if (row == 8) {
            ways++;
            return;
        }
        for (int col = 0; col < 8; col++) {
            int cv = 1 << col;
            if ((cols & cv) != 0)
                continue;
            int ltIndex = 1 << (row - col + 7);
            int rtIndex = 1 << (row + col);
            if ((ltIndex & leftTop) != 0)
                continue;
            if ((rtIndex & rightTop) != 0)
                continue;
            //cols = (byte) (cols |(1<<col));
            cols |= (1 << col);
            leftTop |= ltIndex;
            rightTop |= rtIndex;
            place(row + 1);
            cols &= ~cv;
            leftTop &= ~ltIndex;
            rightTop &= ~rtIndex;
        }
    }
}

这里涉及的位运算技巧还是挺多的,我就先承认自己没掌握好啦

标签:29N,10,int,void,cols,ways,place,皇后,2020
来源: https://www.cnblogs.com/buzhouke/p/13903971.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有