回溯法解决n皇后问题

先上一张图



要求

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

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子松的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值