回溯法及其应用--八皇后问题

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);
	}

}

猜你喜欢

转载自blog.csdn.net/fxjzzyo/article/details/89107131