基本资料同理不罗列了。
不过我发现网上讲这个问题的文章很多,真正讲的清晰易懂的极少,还有些代码乱的一塌糊涂。这里贴篇我认为还可以的文章供参考:
因为这个问题太著名,之前经常在眼睛前面晃过去,所以尽管《数据结构》为此节标了两个星号,我还是忍不住自己实现了下。
有趣的几点感受:
1,虽然这一节放在树这一章里讲,但压根不需要用到树这种数据结构,算是取其神舍其形吧。
2,回溯的概念大体不难理解,但具体用什么形式实现?我想了好久,以至于最后折腾出一堆废代码来(参考第二份源码)。其实,同样根本不需要自己实现,回溯的动作早已隐含在for循环里了。
3,关于此算法的优化改进,网上很多,这里暂时就先不深究了,留待日后学习。
以下是简洁版的代码,运行结果较长,就不贴出了,效率很一般,不过是自己按oop风格写的,也不太难懂:
#include <iostream>
#include <cmath>
using std::cin;
using std::cout;
using std::endl;
class CEightQueen
{
private:
int *result; //记录n皇后问题解的动态数组指针
int length; //皇后数
void PrintResult(); //输出一个解
public:
int total; //解总数
CEightQueen(int n);
~CEightQueen();
void Put(int start_row); //在某行开始落子
int Check(int row, int column); //测试某行某列落子是否合法
};
CEightQueen::CEightQueen(int n)
: total(0)
{
result = new int[n];
length = n;
}
CEightQueen::~CEightQueen()
{
if (result != NULL)
{
delete result;
result = NULL;
}
}
void CEightQueen::Put(int start_row)
{
for(int col=0; col<length; ++col)
{
if (1 == Check(start_row, col))
{
result[start_row] = col; //落子并记录
if ((length - 1) == start_row) //已到最后一行,注意边界行列数比实际行列数少1
{
PrintResult();
total++;
}
else
{
Put(start_row + 1);
}
}
else
{}
}
}
int CEightQueen::Check(int row, int column)
{
if (0 == row)
{
return 1; //第0行随便放永远合法
}
else
{
for (int i=0; i<row; ++i) //0 ~ row-1 行已落子均需与新落子不冲突
{
if ((column == result[i]) || (abs(row - i) == abs(column - result[i])))
{
return 0; //不合法
}
else
{}
}
return 1; //经过前面所有检验后,合法
}
}
void CEightQueen::PrintResult()
{
for(int i=0; i<length; ++i)
{
cout<<result[i]<<" ";
}
cout<<endl;
}
int main()
{
int n;
cout<<"请输入皇后数:";
cin>>n;
CEightQueen eq(n);
eq.Put(0);
cout<<"total : "<<eq.total<<endl;
}
以下是一份有意思的充满冗余的代码。
可以看到,我用记录参数,令函数返回,然后判断返回值决定是继续执行还是结束的做法,实在多此一举:
#include <iostream>
#include <cmath>
using std::cin;
using std::cout;
using std::endl;
class CEightQueen
{
private:
int *result; //记录n皇后问题解的动态数组指针
int length; //皇后数
void PrintResult(); //输出一个解
public:
//int current_row; //回溯到的行数
//int current_column; //回溯到的列数
int total; //解总数
CEightQueen(int n);
~CEightQueen();
int Put(int start_row); //在某行开始落子
int Check(int row, int column); //测试某行某列落子是否合法
};
CEightQueen::CEightQueen(int n)
: //current_row(0),
//current_column(0),
total(0)
{
result = new int[n];
length = n;
//for(int i=0; i<length; ++i)
//{
// result[i] = -1; //初始为-1表示未落子
//}
}
CEightQueen::~CEightQueen()
{
if (result != NULL)
{
delete result;
result = NULL;
}
}
int CEightQueen::Put(int start_row)
{
for(int col=0; col<length; ++col)
{
if (1 == Check(start_row, col))
{
result[start_row] = col; //落子并记录
if ((length - 1) == start_row) //已到最后一行,注意边界行列数比实际行列数少1
{
PrintResult();
total++;
}
else
{
Put(start_row + 1);
}
}
//else
//{
// if ((length -1) == col) //此行直到最后一列均不合法
// {
// //上一行的落子导致下一行无解,需取消上一行结果,从上一行的下一格开始继续落子判断
// //此处返回上一行行数重新Put
// current_row = start_row - 1;
// current_column = result[start_row - 1] + 1;
// return 0; //返回0,表示未结束
// }
// else
// {
// //continue
// }
//}
}
return 1; //finish
}
int CEightQueen::Check(int row, int column)
{
if (0 == row)
{
return 1; //第0行随便放永远合法
}
else
{
for (int i=0; i<row; ++i) //0 ~ row-1 行已落子均需与新落子不冲突
{
if ((column == result[i]) || (abs(row - i) == abs(column - result[i])))
{
return 0; //不合法
}
else
{}
}
return 1; //经过前面所有检验后,合法
}
}
void CEightQueen::PrintResult()
{
for(int i=0; i<length; ++i)
{
cout<<result[i]<<" ";
}
cout<<endl;
}
int main()
{
int n;
cout<<"请输入皇后数:";
cin>>n;
CEightQueen eq(n);
//while( 0 == eq.Put(eq.current_row, eq.current_column));
eq.Put(0);
cout<<"total : "<<eq.total<<endl;
}
最后再贴一份百度贴吧里copy来的代码,写的比较简洁、悬乎,留待日后有空研究。
#include "stdafx.h"
#include <stdio.h>
FILE *fp;
int ha[9], i, a[24], b[24], c[24];
void work(int i)
{
int o, j;
for(j=1; j<9; j++)
{
if(a[j+7]==0 && b[i-j+7]==0 && c[j+i+7]==0)
{
ha[i] = j;
a[j+7] = 1;
b[i-j+7] = 1;
c[j+i+7] = 1;
if (i < 8)
{
work(i + 1);
}
else
{
for(o = 1; o < 9; o++)
{
fprintf(fp, "%d ", ha[o]);
}
fprintf(fp, "\n");
}
a[j+7] = 0;
b[i-j+7] = 0;
c[j+i+7] = 0;
}
}
}
int main()
{
int k;
for(k=0; k<=23; k++)
{
a[k] = 0;
b[k] = 0;
c[k] = 0;
}
fp = fopen("eight.txt","w");
work(1);
fclose(fp);
}