劍指Offer(java版)-8皇后問題

題目:在8*8的國際象棋上擺放8個皇后,使其不能相互攻擊,及任意兩個皇后不得處於同一行,同一列或者贊成對角線上,請問總共有多少種符合條件的擺法。java

 

思路一:算法

因爲八個皇后的任意兩個不能處在同一行,那麼這肯 定是每個皇后佔據一行。因而咱們能夠定義一個數組ColumnIndex[8],數組中第i個數字表示位於第i行的皇后的列號。先把 ColumnIndex的八個數字分別用0-7初始化,接下來咱們要作的事情就是對數組ColumnIndex作全排列。因爲咱們是用不一樣的數字初始化數 組中的數字,所以任意兩個皇后確定不一樣列。咱們只須要判斷獲得的每個排列對應的八個皇后是否是在同一對角斜線上,也就是數組的兩個下標i和j,是否是 i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]。express

 

package cglib;數組

 

public class DeleteNode{
    static int g_number = 0;
    
    public static void EightQueen( )  
    {  
          
        final int queens = 8;  
        int[] ColumnIndex=new int[queens];  
        for(int i = 0 ; i < queens ; ++i)  
            {ColumnIndex[i] = i;    //初始化
            System.out.println("ColumnIndex[i="+i+"]="+ColumnIndex[i]);  
            }
         
        perm(ColumnIndex ,  0,queens-1);  
    }  
    
    public static boolean Check(int ColumnIndex[] , int length)  
    {  
        int i,j;  
        for(i = 0 ; i <= length; ++i)  
        {  
            for(j = i + 1 ; j <= length; ++j)  
            {  
                if( i - j == ColumnIndex[i] - ColumnIndex[j] || j - i == ColumnIndex[i] - ColumnIndex[j])   //在正、副對角線上  
                    return false;  
            }  
        }  
        return true;  
    }  
    
    
    public static void main(String[] args) {  
          
        EightQueen();
       
    }
    
   
    
    public static void Print(int buf[] , int length)  
    {  
        
        System.out.println("g_number="+g_number);
        /*for(int i = 0 ; i <= length; ++i)  
            System.out.print("buf[i="+i+"]="+buf[i]);  
        System.out.println();  */
    }  app

    public static void perm(int[] buf,int start,int end){  
        
          
        if(buf==null || end!=buf.length-1)  
           return ;
        
        if(start==end){
              
            if( Check(buf , end) )   //檢測棋盤當前的狀態是否合法  
            {  
                ++g_number;  
                Print(buf , end);  
            }  
                          
                }  
             
        else{
              
            for(int i=start;i<=end;i++){  
                //System.out.println("交換前:buf[start="+start+"]="+buf[start]);
                //System.out.println("交換前:buf[i="+i+"]="+buf[i]);
                int temp=buf[start];
               
                buf[start]=buf[i];                
                buf[i]=temp;                 
                
                perm(buf,start+1,end);
                  
                temp=buf[start];函數

       
                buf[start]=buf[i];  ui

                buf[i]=temp;
                //System.out.println("變回來:buf[start="+start+"]="+buf[start]);
                //System.out.println("變回來:buf[i="+i+"]="+buf[i]);  this

            }  spa

        }
          .net

    }
   

     }

 

思路2:

   首先,可概括問題的條件爲,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<N}的集合,故又可在上面式子的基礎,概括爲以下:

                   

                    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行不能放棋子。這樣咱們就能寫成下列程序段了:

 

       

[java] view plain copy

 

  1. boolean[] rows = new boolean[8];  
  2.          
  3.   
  4.       for(int m=0;m<N;i++){  
  5.          
  6.   
  7.          rows[cols[m­]]=true;//當前列N的棋子不能放在前面列m的棋子所在行。  
  8.           
  9.   
  10.          int d = N-m;  
  11.   
  12.          
  13.   
  14.         //該句用於設置當前列N的棋子不能放在前面列m的棋子的斜線上  
  15.   
  16.          
  17.   
  18.         if(cols­­-d >= 0)rows[cols­-d]=true;   
  19.   
  20.        
  21.   
  22.        // 該句用於設置當前列N的棋子不能放在前面列m的棋子的反斜線上  
  23.   
  24.         
  25.   
  26.         if(cols+d <=8-1)rows[cols­+d]=true;    
  27.   
  28.   }   
  29.   
  30.          

           

 

 

       好了,到此爲止,咱們程序的核心內容都具有了,一個基於深度優先的遍歷流程和一個判斷位置是否合適的算法。下面貼出運行後算出的全部可行方案(即92種,「+」號表明空棋位,「0」表明皇后所在位置)

 

打印結果

 

 

 

 

package cglib;

 

public class DeleteNode{
       public static int num = 0; //累計方案總數  
        public static final int MAXQUEEN = 8;//皇后個數,同時也是棋盤行列總數  
        public static int[] cols = new int[MAXQUEEN]; //定義cols數組,表示8列棋子擺放狀況  
        public  DeleteNode() {  
           //核心函數  
          getArrangement(0);  
          System.out.println("綜上所述");  
          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<MAXQUEEN;i++){  
           //判斷該行是否合法    
           if(rows[i])continue;  
           //設置當前列合法棋子所在行數  
           cols[n] = i;  
           //當前列不爲最後一列時  
           if(n<MAXQUEEN-1){  
             getArrangement(n+1);  
           }else{  
      
            //累計方案個數  
             num++;  
             //打印棋盤信息  
             printChessBoard();  
           }   
            
            
         }  
           
        }  
        public void printChessBoard(){  
             
           System.out.println("第"+num+"種走法 /n");  
             
           for(int i=0;i<MAXQUEEN;i++){  
             for(int j=0;j<MAXQUEEN;j++){  
               if(i==cols[j]){  
                 System.out.print("0 ");  
               }else  
                 System.out.print("+ ");  
             }  
             System.out.println("/n");  
           }  
             
        }  
        public static void main(String args[]){  
            DeleteNode queen = new DeleteNode();  
        }
   

     }

 

/**
 * 功能:打印八皇后在8*8棋盤上的各類擺法,其中每一個皇后都不一樣行、不一樣列,也不在對角線上。
 * 這裏的「對角線」指的是全部的對角線,不僅是平分整個棋盤的那兩條對角線。

 */

 

 

[java] view plain copy

 

  1. static int GRID_SIZE=8;  
  2.   
  3. /** 
  4.  * 思路:每一行只能擺放一個皇后,所以不須要將棋盤存儲爲完整的8*8矩陣,只需一維數組,其中columns[r]=c表示有個皇后位於r行c列。 
  5.  * @param row 
  6.  * @param columns 
  7.  * @param results 
  8.  */  
  9. public static void placeQueen(int row,Integer[] columns,ArrayList<Integer[]> results){  
  10.     if(row==GRID_SIZE){  
  11.         /*Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. 
  12.          *  The general intent is that, for any object x, the expression:  
  13.             x.clone() != x              will be true. 
  14.          *  and that the expression:  
  15.             x.clone().getClass() == x.getClass()    will be true.  
  16.          *  but these are not absolute requirements. While it is typically the case that:  
  17.             x.clone().equals(x) will be true, this is not an absolute requirement. */  
  18.         results.add(columns.clone());             
  19.     }else{  
  20.         for(int col=0;col<GRID_SIZE;col++){  
  21.             if(checkValid(columns,row,col)){  
  22.                 columns[row]=col;//擺放皇后  
  23.                 placeQueen(row+1, columns, results);  
  24.             }  
  25.         }  
  26.     }  
  27. }  
  28.   
  29. /** 
  30.  * 檢查(row,column)是否能夠擺放皇后,方法: 
  31.  * 檢查有無其餘皇后位於同一列或對角線,沒必要檢查是否在同一行上,由於調用placeQueen時,一次只會擺放一個皇后。由此可知,這一行是空的。 
  32.  * @param columns 
  33.  * @param row 
  34.  * @param column 
  35.  * @return 
  36.  */  
  37. public static boolean checkValid(Integer[] columns,int row,int column){  
  38.     for(int r=0;r<row;r++){  
  39.         int c=columns[r];  
  40.         /* 檢查同一列是否有皇后 */  
  41.         if(c==column)  
  42.             return false;  
  43.           
  44.         /* 檢查對角線: 
  45.          * 若兩行的距離等於兩列的距離,則表示兩個皇后在同一對角線上。 
  46.          */  
  47.         int columnDistance=Math.abs(c-column);  
  48.         int rowDistance=row-r;//row>r,不用取絕對值  
  49.         if(columnDistance==rowDistance)  
  50.             return false;  
  51.     }  
  52.       
  53.     return true;  
  54. }  
相關文章
相關標籤/搜索