回溯法解决N皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。

问题阐述

N皇后问题:将以上问题改为,对于一个任意的N,求是否存在N皇后问题的摆法?如N=1,4,5,6,7,8,9时,存在摆法。


N皇后问题的解法有全排列和回溯两类,其中全排列可以递归与非递归,回溯也可以递归与非递归。于是共有4种解法,下面采用递归的回溯法解决。

算法思路

由于皇后们不能同行、同列和共对角线,每一行都必定放置了一个皇后。故可以采用回溯法从第0行开始放置,若合理,则继续放下一行,一直放到N-1行,若不合理,则回溯到上一状态,换一个位置继续放置。只要存在一种情况能够放到N-1行,并且该行合理,则表示问题有解。
 ①放置第一行的皇后,然后进行②
 ②放置第二行的皇后,先检测自身的方法是否合理。不合理则返回false,回溯到上一状态。合理则继续进行③
 ③…

一直到最后N-1行成功

程序语言描述

解决算法问题必须用恰当的数据结构将其描述出来,这里用一个一维数组int[] a表示。其中a[i]  = j;表示第i行的皇后放在了第j列。(0<=i,j<=N-1)。

Java代码

/* 
 * Created by ping on 2015/8/16.
 */
public class QueensProblem {
    int N;
    int a[];

    public QueensProblem(int N)
    {
        this.N = N;
        a = new int[N];
        for (int y=0;y<N;y++)
        {
           a[y] = -1;
        }
    }

    public boolean hasSolution()
    {
        for (int y=0;y<N;y++)
        {
            if(place(0,y)) {
                System.out.print(N+"皇后问题的一个解:");
                for(int index = 0;index<N;index++)
                    System.out.print(a[index] + " ");
                System.out.println("");
                return true;
            }
        }
        return false;
    }

    /*place(i,j)表示把i行的皇后放到第j列*/
    public boolean place(int i,int j)
    {
        if(i<0||i>=N||j<0|j>=N)
            return false;
        a[i] = j;       //把第i行的皇后放在第j列

        for(int x=0;x<i;x++)//先检测自身是否合理
        {
            for(int y=x+1;y<=i;y++)
            {
                if(a[x]==a[y]||Math.abs(a[x]-a[y])==Math.abs(x-y)) {
                    a[i] = -1;  //若不合理,则回退
                    return false;
                }
            }
        }
        if(i==N-1)  return true;    //之前漏了这一行!!!如果没有这一行,这违背了递归算法的原则

        for (int y=0;y<N;y++)
        {
            if(place(i+1,y)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String args[])   {
        for(int x = 1;x<10;x++)
        {
            new QueensProblem(x).hasSolution();
        }
    }
}


代码中第50行讲到了递归算法的原则,
编写递归代码必须符合以下三点,违背其中任意一条都可能得到错误的结果或者低效的代码:

a.递归总有一个最简单的情况

b.递归总是尝试去解决一个规模更小的问题

c.父问题和子问题不应该有交集

我之前写这个代码的时候调试老是出错,用了1个小时,结果发现是由于违背了条件a,竟然漏掉了对最简单的一种情况的处理。

    原文作者:回溯法
    原文地址: https://blog.csdn.net/wuqiuping695/article/details/47703419
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞