回溯法学习_四皇后问题

回溯法思想:在包含问题的所有解的解空间数中,按照深度优先的策略。从根节点出发深度探索解空间数。当探索到某一点时,要先判断该结点是否包含问题的解,如果包含就从该节点继续探索下去;如果该节点不包含问题的解,那就说明以该节点为根节点的子树不一定包含问题的最终解,因此需要跳过以该节点为根节点的子树的探索,逐层向其祖先节点回溯,这个过程也叫做解空间树的剪枝操作。
引用回溯法求解问题的所有解,要回溯到解空间树的树根,这样才能保证根节点的所有子树都被探索到才结束。
通过回溯法解决一个四皇后问题,甚至八皇后等问题。
问题: 给定一个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;                                    
}

运行结果:

  • 15
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值