八皇后
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;
}
}
}
}
问题描述:
要求:
算法思想:
代码如下:
* n后问题求解
************************************************************************/
#include < stdio.h >
#include < stdlib.h >
#include < math.h >
#include < conio.h >
#define MAXNUMBER 20
// 判断当前得到的解向量是否满足问题的解
bool place_queen( int x[], int k)
{
int i;
for(i=1;i<k;i++)
{
if((x[i]==x[k]) || (abs(x[i]-x[k])==abs(i-k)))
return false;
}
return true;
}
// 将结果简单信息打印到屏幕
void output_queens( int x[], int n)
{
for(int i=1;i<=n;i++)
printf("%3d",x[i]);
printf("");
}
// 将结果详细信息写入文件
void output_queens(FILE * fp, int number, int x[], int n)
{
fprintf(fp,"solution %d:",number);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(j==x[i])
fprintf(fp,"1 ");
else
fprintf(fp,"0 ");
}
fprintf(fp,"");
}
fprintf(fp,"");
}
/************************************************************************
* n后问题求解
* input : n, the number of queens
* output : the vector of solution, X
************************************************************************/
int n_queens(FILE * fp, int n, int x[])
{
int nCount=0; //解个数
int k=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--; //回溯到前一个皇后
}
}
return nCount;
}
int main()
{
int n=8,x[MAXNUMBER]={0};
FILE *fp=fopen("8皇后问题的解.txt","w");
if(fp==NULL)
{
printf("can not wirte file!");
exit(0);
}
printf("the queens are placed on the coloums :");
//求解并写入文件
int nCount=n_queens(fp,n,x);
printf("there are %d solutions!",nCount);
fclose(fp);
getch();
return 0;
}
与其他方法对比:
#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;
}