八皇后问题的回溯法。



八皇后问题:

   问题背景:国际象棋是一个8*8的矩阵,在棋盘中同时放下8个皇后,且互相不攻击的情况叫八皇后问题

  

看看这张动态图片,可对八皇后问题的回溯法在概念上了解不少。

https://img-blog.csdn.net/20140416231902046


//.回溯算法的基本思想:从问题的某一种状态出发,搜索可以到达的所有状态。

//.在递归过程中,若当前状态无法满足要求,可向前回退,并继续搜索其他可达状态。当所有状态都到达后,回溯算法结束!


回溯法在这个问题中。其实就是不断的通过递归函数,去往棋盘中尝试放皇后,成功就继续递归(即继续放皇后),失败就跳出递归函数,回溯到上层递归函数中,上层递归函数中保存着上一个皇后的位置!!!这就是八皇后中,回溯的概念!


算法思路: 一,为了模拟实际情况,定义如下数组,来解决问题。

int queen[8],b[8],c[15],d[15];

首先,queen[8]是表示“行”,八个皇后对应八个不同的行号;。所以其实并不是非要使用二维数组来模拟,一维数组也可以解决问题。

queen[i]记录着第i+1行的皇后所处的列号,即它在第几列。取值为0~7;


还有就是,如果一个位置为皇后占据,那么它所在的 行、列、45度对角线与135度对角线都应该被标记成“禁区”,所以还需要模拟出

这种“禁区”效果来。

(1)行线禁区。queen[i]实际上就是再说明第i+1行皇后所处的位置。例如queen[3]是用来保存第四行的列号。若给queen[3]赋值为5,就表示把一个皇后放在了第4行第5列。同时也表示该行已经无法在放置第二个皇后。所以当递归到i的时候,之前的行都已经被占领,无需再明显表示。

(2)列线禁区。第4行第5列中放置一个皇后之后,第5列不能再放置第二个皇后。定义一个数组b[j],用来表示第j+1列有无皇后,只有0或1两种数值,

0表示无皇后,1表示有有皇后。

(3)45度对角线禁区。在第4行第5列放置一个皇后之后,该位置所在的45度对角线就变为禁区。棋盘上总共有15条45度对角线。每一条45度线上的

行号和列号的和值(i+j)都是一个常量,取值范围为0~14。程序中定义数组c[15],来表示45度线禁区。其中c[i+j]记录从棋盘左上角数第(i+j+1)条对角线上有无皇后。只有0或1两种数值,0表示无皇后,1表示有有皇后。

(4)135度对角线禁区。定义类同45度线禁区,定义数组d[15]。棋盘上总共15条135度对角线,每一条135度线的行号与列号的差值(i-j)为一个常量,取值为-7~7,所以从其右上角开始标记,d[i-j+7]表示第i-j+8条135度对角线上有无皇后。只有0或1两种数值,0表示无皇后,1表示有有皇后。


程序定义了全局变量

int queennum=0;

表示当前皇后所处的行号,也表示递归的层次(即当前递归,使问题处理到了到了第几行)。

程序定义了两个函数,原型为:

void prinf();
void tryqueen(int  i);

其中,prin()的功能是打印八皇后问题的当前解。输出解的序号以及从1到8行的八个皇后所处的列号。

tryqueen()的功能是为第i个皇后选择合适的位置,也就是为第i+1行放置一个皇后。


初始化棋盘,全部为0表示全部未被占领。

for(k=0;k<15;k++){                   
		 b[k]=0;
		 c[k]=0;
  	  	d[k]=0;
	  }

基本注释就是这些,下面贴上源代码。

#include<stdio.h>                      
#include<stdlib.h>                    
  int queen[8],b[8],c[15],d[15];
  int queennum=0;

  void print(){                    //打印当前的结果
	  int k;
	  queennum++;
	  printf("\t%d: ",queennum);
	  for(k=0;k<8;k++)
		  printf("%d ",queen[k]);
	  if(queennum%3==0)
		  printf("\n");
  }


  void tryqueen(int i){                   
	  int j;
	  for(j=0;j<8;j++){           //每个皇后都有八种可能位置
		  if((b[j]==0)&&(c[i+j]==0)&&(d[i-j+7]==0)){     //判断位置是否被占领,没被占领则执行下一步。
			  queen[i]=j+1;       //摆放第i+1行的皇后到j+1列
			  b[j]=1;             //宣布占领了第j+1列
			  c[i+j]=1;           //宣布占领了来两个对角线
			  d[i-j+7]=1;
			  if(i<7)             //若8个皇后未摆满,则继续摆放下一个皇后。
				  tryqueen(i+1);
			  else
				  print();        //完成任务,打印当前结果
			  
			  b[j]=0;             //回溯,将本次所标志的“禁区”取消,下一次将会把皇后摆放到下一列。     
			  c[i+j]=0; 
			  d[i-j+7]=0;
		  }
	  }
  }
  
  int main (void){
	  int k;
	  printf("\t八皇后问题解:\n");
	  for(k=0;k<15;k++){           //初始化棋盘  
		 b[k]=0;
		 c[k]=0;
  	  	d[k]=0;
	  }
	  tryqueen(0);                //启动递归函数。
	  system("PAUSE");
	  return 0;
  }
		  

			  

八皇后问题总共有92种解。运行结果如下。






所以,回溯法的实质,就是检测所有可能的解,也就是穷尽所有可能的情况,从中寻求问题的答案。
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值