回溯法解决N皇后问题

来源:互联网 发布:优化探究官网 编辑:程序博客网 时间:2024/05/17 20:31

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于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,竟然漏掉了对最简单的一种情况的处理。



0 0