回溯算法之八皇后问题

N 皇后问题是在N×N 的国际象棋棋盘上, 放置N 个皇后,使任何两个皇后都不能相互攻击。也就是说, 任何两个皇后, 都不在同一行、同一列或同一斜线上。N 皇后问题是由八皇后问题演化而来的。八皇后问题于1848 年由国际象棋棋手MaxBazzel 提出。八皇后问题自从提出之后, 吸引了许多著名数学家的关注, 其中包括Carl Gauss。近年来, N 皇后问题成为计算机应用领域内的一个经典问题, 不但成为测试算法的一个工具, 还成为检验并行计算系统效率的一个有效工具。


问题定义


八皇后问题是一个非常著名的问题,最初是由著名数学家高斯提出的。问题的描述是这样的:在一个8 ×8的棋盘上,摆放8个皇后,任意两个皇后不能处在同一行、同一列和同一斜线上。该问题也可以被扩展为在一个n ×n的棋盘上摆放n个皇后的问题。

在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。


ss

经典的回溯算法,该算法的思路如下:

•    依次在棋盘的每一行上摆放一个皇后。 •    每次摆放都要检查当前摆放是否可行。如果当前的摆放引发冲突,则把当前皇后摆放到当前行的下一列上,并重新检查冲突。

•    如果当前皇后在当前行的每一列上都不可摆放,则回溯到上一个皇后并且将其摆放到下一列上,并重新检查冲突。

•    如果所有皇后都被摆放成功,则表明成功找到一个解,记录下该解并且回溯到上一个皇后。

问题分析


解向量:(x1, x2, … , xn),x[i]表示皇后i放在棋盘的第i行的第x[i]列。

显约束:xi=1,2, … ,n

隐约束:
          1)不同列:xi≠xj (i≠j)
          2)不处于同一正、反对角线:|i-j|≠|xi-xj|

解空间:排列树

约束函数:xi≠xj (i≠j) and |i-j|≠|xi-xj|


算法描述

//place函数测试若将皇后k放在x[k]列是否与前面已放置的k-1个皇后都不在同一列,而且都不在同一斜线上。
bool Queen::Place(int k)
{
  for (int j=1;j<k;j++)
    if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) return false;
  return true;



//递归函数backtrack(1)实现对整个解空间的回溯搜索。
void Queen::Backtrack(int t)
{
  if (t>n) sum++;//sum记录当前已找到的可行方案数。
  else
    for (int i=1;i<=n;i++) {
      x[t]=i;
      if (Place(t)) Backtrack(t+1);
    }
}

当i<=n时,当前扩展结点是解空间的一个内部结点。该结点有x[i]=1,2,…n共n个儿子结点。对当前扩展结点的每一个儿子结点,由函数place检查其可行性,并以深度优先的方式递归地对可行子树进行搜索,或者剪去不可行子树。


下面是实现的四皇后的Java代码


public class Empress {
	
	public static int num=0;
	
	public List<Integer>list=new ArrayList<Integer>();
	
	public boolean constraint(int []array,int k)
	{
		for(int i=1;i<k;i++)
		{
			if(Math.abs(array[i]-array[k])==Math.abs(i-k)||array[i]==array[k])//如果在同一个斜线或者在同一列
			{
				return false;
			}
		}
		return true;
	}
	public void search(int n,int []array)
	{
		if(n>4)
		{
			num++;
			for(int i=1;i<array.length;i++)
			{
				list.add(array[i]);
			}
		}
		else
		{
			for (int i=1;i<array.length;i++) {
			      array[n]=i;
			      if (constraint(array,n))
			    	  search(n+1,array);
			    }

		}
	}
	
	public void print()
	{
		Iterator iterator=list.iterator();
		while(iterator.hasNext())
		{
			for(int i=0;i<4;i++)
			{
				System.out.print(iterator.next()+" ");
			}
			System.out.println();
		}
	}
	
	public static void main(String args[])
	{
		Empress e=new Empress();
		int []array=new int [5];
		e.search(1, array);
		e.print();
	}
    

}


N后问题的时间复杂性


如果只要找出N后问题的一个解,那么这是一个求路径的问题。

求路径:只需要找到一条路径便可以得到解。设每个状态有k个后继,其搜索树为K叉树,其结点总数为k(n+1)–1【k的n+1次方】,遍历的时间为O(kn),这里n为找到解的路径长度。

N后问题的路径深度为n,可选择的后继有n个,所以时间复杂性为O(nn)。【n的n次方】
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值