回溯法解决n皇后问题

来源:互联网 发布:生活的乐趣 知乎 编辑:程序博客网 时间:2024/05/18 01:08

先上一张图



要求

1.一行只能放一个皇后,所以我们一旦确定此处可以放皇后,那么该行就只能放一个皇后,下面的就不要再搜了。

2.每一列只能放一个皇后,所以我们下次搜索就不要再搜已经放过的皇后了。

3.斜的45°线也只能放一个。


算法思路

  首先我们分析一下问题的解,我们每取出一个皇后,放入一行,共有八种不同的放法,然后再放第二个皇后,同样如果不考虑规则,还是有八种放法。于是我们可以用一个八叉树来描述这个过程。从根节点开始,树每增加一层,便是多放一个皇后,直到第8层(根节点为0层),最后得到一个完全八叉树。  

  紧接着我们开始用深度优先遍历这个八叉树,在遍历的过程中,进行相应的条件的判断。以便去掉不合规则的子树。

  那么具体用什么条件来进行子树的裁剪呢?

  我们先对问题解的结构做一个约定。

  用X[i]来表示,在第i行,皇后放在了X[i]这个位置。

  于是我们考虑第一个条件,不能再同一行,同一列于是我们得到x[i]不能相同。剩下一个条件是不能位于对角线上,这个条件不是很明显,我们经过分析得到,设两个不同的皇后分别在j,k行上,x[j],x[k]分别表示在j,k行的那一列上。那么不在同一对角线的条件可以写为abs((j-k))!=abs(x[j]-x[k]),其中abs为求绝对值的函数。

  于是下面我们便可以利用一个递归的调用来遍历八叉树。


        private void button1_Click(object sender, EventArgs e)        {                   int n = 4;                   N_Queen (n);                       }        public bool  Place(int[] Column, int index)        {            int i;            for (i = 1; i < index; i++)            {                //求列的差值和行的差值                int Column_differ = System.Math.Abs(Column[index] - Column[i]);                               int Row_differ = System.Math.Abs(index - i);                //检查列或对角线上是否有冲突                if (Column[i] == Column[index] || Column_differ == Row_differ)                     return false ;    //有冲突,位置放置的不正确            }            return true ; //没有与皇后同行、同列或者同对角线        }        void N_Queen(int n)        {            int i;                                         int answer_num = 0; //方案计数              int[] Column_Num = new int[n + 1];             for (i=1;i<=n;i++)                Column_Num [i]=0; //四皇后全放在第0列            int index = 1; //从第一行的第一个皇后开始            while (index > 0)            {                //第一个皇后安放在的第一行第一列                Column_Num[index]++;                //检查与第index个皇后是否相互攻击                while (Column_Num[index] <= n && !Place(Column_Num, index))                      Column_Num[index]++; //有冲突,第index个皇后右移一列                //无冲突,找下一个皇后,或者最后一个皇后放置完毕                if (Column_Num[index] <= n)                     {                    if (index == n)  //最后一个皇后放置成功                    {                        answer_num++;                        textBox1 .Text  =textBox1 .Text  + "\r\n" + "方案" + answer_num;                        for (i = 1; i <= n; i++)                        {                           textBox1 .Text  = textBox1 .Text  + "(" + i + "," + Column_Num[i] + ")";                        }                        for (i = 1; i <= n; i++)                            Column_Num[index]++;                    }                    else     //继续寻找下一个皇后的位置                    {                        index++;                        Column_Num[index] = 0;                    }              }                else       //当前皇后无法安置,回溯                    index--;   //当前皇后回归0列,回溯到前一行皇后            }        }

结语

     n皇后问题是回溯的经典例子,而回溯找到界限函数是重点,界限函数可以避免移动到不可能产生解的子空间。