回溯法及其應用--八皇后問題

https://www.cnblogs.com/bigmoyan/p/4521683.html
https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html
https://blog.csdn.net/qq_32400847/article/details/51474105#commentBox

1. 回溯法

回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇。
這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。

所謂回溯法,名字高大上,思想很樸素。設想把你放在一個迷宮裏,想要走出迷宮,最直接的辦法是什麼呢?沒錯,試。先選一條路走起,走不通就往回退嘗試別的路,走不通繼續往回退,直到找到出口或所有路都試過走不出去爲止。

2. 八皇后問題

是一個古老而著名的問題,是回溯算法的典型案例。
該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。

憋猜了,正確答案92種。

詳見這篇文章:
https://www.cnblogs.com/bigmoyan/p/4521683.html
博主寫得圖文並茂,通俗易懂,風趣幽默,真好!

直接上代碼吧,我這裏把那位博主的代碼豐富了一下:不僅打印方案數,還能以兩種方式將擺放圖案輸出。

  • 一種是直接打印方案;
  • 另一種是將方案組織到列表裏輸出(在leetcode類的編程題中會有這種要求)。leetcode51題-N皇后問題

Java代碼

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

public class 八皇后問題方法二 {

	//記錄方案數
	private int count = 0;
	
	//記錄每一行皇后的位置
	private int[] queen = new int[8];
	
	//記錄方案的列表
	private List<List<String>> results = new ArrayList<>();
	
	/**
	 * 主要的回溯函數
	 * @param row
	 */
	public void queen(int row){
		if(row == 8){
			//統計方案數
			count++;
			//打印方案
			showAnswer();
			//將結果統計成列表形式
			generateAnswer();
		}else{
			for(int j = 0;j<8;j++){
				queen[row] = j;
				if(isOk(row))
					queen(row+1);
			}
		}
	}
	
	/**
	 * 將結果組織成列表的形式
	 */
	private void generateAnswer(){
		List<String> ans = new ArrayList<>();
		for(int i = 0;i<8;i++){
			StringBuilder sb = new StringBuilder();
			for(int j = 0;j<8;j++){
				if(queen[i] == j){
					sb.append("Q");
				}else{
					sb.append(".");
				}
			}
			ans.add(sb.toString());
		}
		results.add(ans);
	}
	
	/**
	 * 打印合法的結果
	 */
	private void showAnswer() {
		System.out.println("答案是:");
		for(int i = 0;i<8;i++){
			for(int j = 0;j<8;j++){
				if(queen[i] == j){
					System.out.print("Q");
				}else{
					System.out.print(".");
				}
			}
			System.out.println();
		}
		
	}

	/**
	 * 判斷是否能放置皇后
	 * @param row
	 * @return
	 */
	private boolean isOk(int row) {
		//依次判斷前面的行中與當前行放置的皇后是否衝突
		for(int j = 0;j!=row;j++){
		//在同一列、在副對角線(右上到左下)、在主對角線(左上到右下),都是衝突的
			if(queen[row]==queen[j] || row+queen[row] == j+queen[j]|| row-queen[row] == j-queen[j]){
				return false;
			}
		}
		
		return true;
	}

	public static void main(String[] args) {
		八皇后問題方法二 qu = new 八皇后問題方法二();
		qu.queen(0);//進行計算
		System.out.println(qu.count);//打印方案數
		System.out.println(qu.results);//以列表的形式打印方案

	}

}


另外,你可能注意到上面代碼裏的類名字是方法二了。是的,還有另一種寫法。
不過,個人感腳沒有上面那種好理解。

不妨也看一下,思想都是一樣的回溯法。不同的是對合法放置的判斷。

上面的方法使用了一個isOk函數,
下面這個用了兩個數組分別存放主對角線和副對角線是否有皇后。關於這個解釋,參考下面的文章,應該可以理解一哈吧

參考文章:
https://blog.csdn.net/qq_32400847/article/details/51474105#commentBox

代碼參考了百度百科:八皇后問題

Java代碼


public class 八皇后問題 {

	private int[] column; //同欄是否有皇后,1表示有
    private int[] rup; //右上至左下是否有皇后
    private int[] lup; //左上至右下是否有皇后
    private int[] queen; //解答
    private int num; //解答編號
    
    
    public 八皇后問題(){
    	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 = new 八皇后問題();
        queen.backtrack(1);
	}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章