`
yidongkaifa
  • 浏览: 4060926 次
文章分类
社区版块
存档分类
最新评论

Algorlthm gosslp:八皇后问题浅谈

 
阅读更多

八皇后

8*8的棋盘上放八个皇后棋子,要求在纵行,竖行和斜行的八个方向上都不能有两个以上的皇后。1970和1971年,E.W.Dijkstra与N.Wirth曾用这个问题讲解程序设计技巧,淡然,像我这码农级程序员很难理解怎样应用到程序设计技巧中,现阶段只管理解问题的解决方法。

八皇后问题主要使用递归求解,然而如何减少递归次数呢?

在每次递归时,不必要所有的都检查过,例如若某列检查过,该列的其他格子就不在检查了,这个方法称为分支修剪。

程序中风别在横竖八个地方和斜方向15个地方做了标记,若有皇后则递归返回。本人觉得斜方向的标记控制和递归的调用时问题的精华。

C语言实现:

#include<stdio.h>
#include<stdlib.h>
#define N 8

int column[N+1];
int rup[2*N+1];
int lup[2*N+1];
int queen[N+1]={0};
int num;

void backtrack(int);

int main(void){
	int i;
	num = 0;
  	for(i=1;i<=N;i++)
		column[i] = 1;
	for(i=1;i<=2*N;i++)
		rup[i] = lup[i] =1;
	backtrack(1);
	return 0;
}

void showAnswer(){
	int x,y;
	printf("\n解答%d\n",++num);
	for(y=1;y<=N;y++){
		for(x=1;x<=N;x++){
			if(queen[y]==x){
				printf(" Q");
			}else{
				printf(" .");
			}
		}
		printf("\n");
	}
}

void backtrack(int i){
	int j;

	if(i>N){
		showAnswer();
	}else{
		for(j= 1;j<=N;j++){
			if(column[j] == 1&&lup[i-j+N] == 1){
				queen[i] = j;
				column[j] = rup[i+j] = lup[i-j+N] = 0;
				backtrack(i+1);
				column[j] = rup[i+j] = lup[i-j+N] = 1;
			}
		}
	}
}









问题描述:

八皇后问题是大数学家高斯于1850年提出来的。该问题是在8×8的国际象棋棋盘上放置8个皇后,使得没有一个皇后能“吃掉”任何其他一个皇后,即没有任何两个皇后被放置在棋盘的同一行、同一列或同一斜线上。

要求:

编一个程序求出该问题的所有解。

算法思想:

回溯法
使用回溯算法求解的问题特征,求解问题要分为若干步,且每一步都有几种可能的选择,而且往往在某个选择不成功时需要回头再试另外一种选择,如果到达求解目标则每一步的选择构成了问题的解,如果回头到第一步且没有新的选择则问题求解失败。
该问题也可扩展到N后问题求解,只需修改程序main函数中的n值即可。

代码如下:

/************************************************************************
*n后问题求解
***********************************************************************
*/


#include
<stdio.h>
#include
<stdlib.h>
#include
<math.h>
#include
<conio.h>

#defineMAXNUMBER20

//判断当前得到的解向量是否满足问题的解
boolplace_queen(intx[],intk)
{
inti;
for(i=1;i<k;i++)
{
if((x[i]==x[k])||(abs(x[i]-x[k])==abs(i-k)))
returnfalse;
}


returntrue;
}


//将结果简单信息打印到屏幕
voidoutput_queens(intx[],intn)
{
for(inti=1;i<=n;i++)
printf(
"%3d",x[i]);

printf(
"");
}


//将结果详细信息写入文件
voidoutput_queens(FILE*fp,intnumber,intx[],intn)
{
fprintf(fp,
"solution%d:",number);
for(inti=1;i<=n;i++)
{
for(intj=1;j<=n;j++)
{
if(j==x[i])
fprintf(fp,
"1");
else
fprintf(fp,
"0");
}

fprintf(fp,
"");
}

fprintf(fp,
"");
}


/************************************************************************
*n后问题求解
*input:n,thenumberofqueens
*output:thevectorofsolution,X
***********************************************************************
*/

intn_queens(FILE*fp,intn,intx[])
{
intnCount=0;//解个数
intk=1;//先处理第1个皇后
x[1]=0;

while(k>0)
{
x[k]
=x[k]+1;//在当前列加1的位置开始搜索

while(x[k]<=n&&!place_queen(x,k))//当前列位置是否满足条件
x[k]=x[k]+1;//不满足,继续搜索下一列位置

if(x[k]<=n)//若存在满足条件的列
{
if(k==n)//是最后一个皇后,则得到一个最终解
{
//break;//此处若break,则只能得到一个解
nCount++;
output_queens(x,n);
//输出
output_queens(fp,nCount,x,n);
}

else//否则,处理下一个皇后,即第k+1个皇后
{
k
++;
x[k]
=0;
}

}

else//若不存在满足条件的列,则回溯
{
x[k]
=0;//第k个皇后复位为0
k--;//回溯到前一个皇后
}

}


returnnCount;
}


intmain()
{
intn=8,x[MAXNUMBER]={0};

FILE
*fp=fopen("8皇后问题的解.txt","w");
if(fp==NULL)
{
printf(
"cannotwirtefile!");
exit(
0);
}


printf(
"thequeensareplacedonthecoloums:");
//求解并写入文件
intnCount=n_queens(fp,n,x);
printf(
"thereare%dsolutions!",nCount);
fclose(fp);
getch();

return0;
}

与其他方法对比:

#include <stdio.h>
#include <stdlib.h>
#define max 8
int queen[max], sum=0; /* max为棋盘最大坐标 */

void show() /* 输出所有皇后的坐标 */
{
int i;
printf("(");
for(i = 0; i < max; i++)
{
printf(" %d", queen[i]);
}
printf(")\n");
sum++;
}

int PLACE(int n) /* 检查当前列能否放置皇后 */
{
int i;
for(i = 0; i < n; i++) /* 检查横排和对角线上是否可以放置皇后 */
{
if(queen[i] == queen[n] || abs(queen[i] - queen[n]) == (n - i))
{
return 1;
}
}
return 0;
}

void NQUEENS(int n) /* 回溯尝试皇后位置,n为横坐标 */
{
int i;
for(i = 0; i < max; i++)
{
queen[n] = i; /* 将皇后摆到当前循环到的位置 */
if(!PLACE(n))
{
if(n == max - 1)
{
show(); /* 如果全部摆好,则输出所有皇后的坐标 */
}
else
{
NQUEENS(n + 1); /* 否则继续摆放下一个皇后 */
}
}
}
}

int main()
{
NQUEENS(0); /* 从横坐标为0开始依次尝试 */
printf("%d", sum);
system("pause");
return 0;
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics