回溯法思想:在包含问题的所有解的解空间数中,按照深度优先的策略。从根节点出发深度探索解空间数。当探索到某一点时,要先判断该结点是否包含问题的解,如果包含就从该节点继续探索下去;如果该节点不包含问题的解,那就说明以该节点为根节点的子树不一定包含问题的最终解,因此需要跳过以该节点为根节点的子树的探索,逐层向其祖先节点回溯,这个过程也叫做解空间树的剪枝操作。
引用回溯法求解问题的所有解,要回溯到解空间树的树根,这样才能保证根节点的所有子树都被探索到才结束。
通过回溯法解决一个四皇后问题,甚至八皇后等问题。
问题: 给定一个4 * 4的棋盘,现在要向棋盘中放入n个皇后,使任意的两个皇后都不在同一行、同一列或同一条对角线上,问总共有多少种放法?
思考:
1,按照回溯法的思想,我们需要一层一层的思考,对于四行四列的棋盘,我们可以考虑第一列的四个位置,第一列的每个位置又延伸出第二列的每个位置,现在相当于考虑完了前两列的所有情况,依次类推,通过第二列的每个位置延伸出第三列的每个位置,最后就想当于考虑完了棋盘中的所有情况也就是4* 4*4 * 4种。
2,所有的情况考虑了,但是还有一个至关重要的问题就是判断位置上是否可以放置皇后,如果说第一行第一列的位置放置了皇子,那么第一行第二列和第二行第二列就不能够放置皇子了,所以需要添加一个函数嵌套在判断语句当中,这样就方便对每个位置的判断。
大体思路就是以上说明的。
详细思路还需要在细分思考:
1,函数怎样实现一层一层的探索确定?当我们确定了第一列第一个位置是皇后,那么第一列第二个位置就不需要考虑了,因为他所延伸下的树都不满足题干要求,因为它的祖宗第一列就不满足,对于第二列第三列都是类似的道理,如果该列某一位置不满足题干的要求,那么该位置所延伸的子结构就都不需要思考了。这也正好贯穿了回溯法的思想;
2,我们的要求是考虑到棋盘的每行每列,并且4个皇后也正好贯穿了棋盘的四行四列,所以我们必须考虑完整个树的情况才可以结束,当第一列某一个位置可以放置皇后,那么就要考虑该位置的后一列放置情况了,我们需要列数加1,行数循环考虑,这与第一列的考虑方式是相同的,第一列就是列数不变行数递增考虑,说明这样每一列递增的函数功能是相同的,只是参数列数不同,很明显这就是一个递归的问题,四个本函数嵌套就考虑完了所有的列,也就可以输出一次确定的位置。
3,还有一个问题就是函数的某一位置是否可以放置皇后,这就需要另外在加一个函数,某一位置放置了皇后,该位置的行 ,列,对角线都不可以放置皇后,如果该位置不符合哪一要求就返回0,表示不可以放置皇后,这就一共有六种情况,行,列,左上,左下,右上,右下;
实验代码:
#include <stdio.h>
#include <stdlib.h>
#define N 4
int sum=0;
/* run this program using the console pauser or add your own getch, system("pause") or input loop*/
void queen(int j,int (*p)[N]);
int isplace(int k,int j,int (*p)[N]);
int main(int argc, char *argv[]) {
int p[N][N];
int i,j;
for(i=0;i<N;i++){
for(j=0;j<N;j++){
p[i][j]=0;
}
}
printf("表示出皇后可能在的位置:\n");
queen(0,p);
printf("皇后放置情况的种类数:%d",sum);
return 0;
}
void queen(int j,int (*p)[N]){
int i,m,k;
if(j==N){
for(i=0;i<N;i++){
for(m=0;m<N;m++){
printf("%d ",p[i][m]);
}
printf("\n");
}
sum++;
printf("\n");
}
for(k=0;k<N;k++){ //其中k代表行
if(isplace(k,j,p)){
p[k][j]=1; //确定第一个皇后的位置;
queen(j+1,p);
p[k][j]=0; //如果下一列的遍历无满足的位置,则回溯重置;
}
}
}
int isplace(int k,int j,int (*p)[N]){
int m,n;
for(m=j,n=0;n<N;n++){ //判断同列是否有相同皇后
if((p[n][m]==1)&&(n!=k)){
return 0;
}
}
for(m=k,n=0;n<N;n++){ //同行
if((p[m][n]==1)&&(n!=j)){
return 0;
}
}
for(m=k-1,n=j-1;m>=0 && n>=0;m--,n--){ //左上方
if(p[m][n]==1){
return 0;
}
}
for(m=k+1,n=j+1;m<N && n<N;m++,n++){ //右下方
if(p[m][n]==1){
return 0;
}
}
for(m=k-1,n=j+1;m>=0 && n<N;m--,n++){ //右上方
if(p[m][n]==1){
return 0;
}
}
for(m=k+1,n=j-1;m<N && n>=0;m++,n--){ //左下方
if(p[m][n]==1){
return 0;
}
}
return 1;
}
运行结果: