八皇后问题:
问题背景:国际象棋是一个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种解。运行结果如下。
所以,回溯法的实质,就是检测所有可能的解,也就是穷尽所有可能的情况,从中寻求问题的答案。