N皇后问题,做之前感觉挺难,但做着做着发现其实还挺简单的。用递归写的代码简直不要太简洁!
在这里我列出了四种解法,具体讲解见注释:
C语言解法
C++解法
C语言解法升级版
C++解法升级版
其中普通版利用的是二维数组,升级版用的是一维数组。
用二维数组来做直观,好理解,但效率比用一维数组低。
感觉 面向对象的C++ 的确和 面向过程的C 在思想上有较大区别,以前真的是拿C++当C用,,,
话不多说,开始解题。
(用到了我之前从来没用过的全局变量)
(不过老师上课好像讲过不建议定义全局变量。。)
#include<stdio.h>
#define maxsize 20
#define occupied 1
#define not_occupied 0
/** 全局变量定义棋盘,
* 这样调用函数时就不用把这个二维数组传来传去了。**/
bool queen_square[maxsize][maxsize]={not_occupied};
int size;
int answer_num=1;
/**三个函数分别用来:
* 检查当前位置可不可以放
* 打印棋盘
* 递归求解 **/
bool unguarded(int row, int col);
void print();
void solve(int row);
unguarded函数:判断当前位置是否可以放
(因为我写的这个递归是从最下面的一行开始放皇后,所以要检查的是当前行的“下面的”棋盘)
bool unguarded(int row, int col)
{
bool ok=true;
int i=0;
for(i=0 ; i<size && ok ;i++)//检查该列
{
if(queen_square[i][col]==occupied)
ok=false;
}
for(i=1 ; row+i<size&&col+i<size&&ok ; i++)//检查指向左下的对角线
{
if(queen_square[row+i][col+i]==occupied)
ok=false;
}
for(i=1 ; row+i<size&&col-i>=0&&ok ; i++)//检查指向右下的对角线
{
if(queen_square[row+i][col-i]==occupied)
ok=false;
}
return ok;
}
print函数:打印棋盘,无需赘述。
void print()
{
printf("THE %d ANSWER:\n\n",answer_num++);
for(int i=0;i<size;i++)
{
for(int j=0;j<size;j++)
{
if(queen_square[i][j]==occupied)
printf(" O ");
else printf(" + ");
}
printf("\n\n");
}
printf("\n\n");
}
solve函数:递归函数,在第row行放皇后。
void solve(int row)
{
if(row<0)//row=-1,说明第零行已经放上了皇后,所以此时要把棋盘打印出来
print();
else
{
for(int i=0; i<size ;i++)
if(unguarded(row,i)) //实现回溯的剪枝操作:
//如果(row,i)这个位置可以放皇后的话 才进行下面的操作:
//放皇后,然后找基于这个所放位置的全部解。
{
queen_square[row][i]=occupied;//(row,i)放上皇后
solve(row-1); //然后在row-1行放皇后
queen_square[row][i]=not_occupied;//把基于(row,i)的解都求出来之后,移除(row,i)上的皇后
//下一轮循环会找基于(row,i+1)的解
}
}
}
主函数:
int main(){
printf("Please input the size of the queen_square.\nThe size should between 1 and 20.\n");
scanf("%d",&size);
while(size<=0||size>maxsize)
{
printf("Please input a valid size!\n");
scanf("%d",&size);
}
solve(size-1);//solve()中的参数是行坐标,所以要减一,
//否则棋盘就多了一行,此时解的个数会变成真正的解的个数的size倍。
return 0;
}
#include<iostream>
#define MAXSIZE 20
#define not_occupied 0
#define occupied 1
using namespace std;
Queen(类)的定义:
(不太明白为什么Queen里的函数如果定义成除了int之外别的类型会报错呢?脑壳痛,,周三问问助教好了,,)
class Queens{
public:
Queens(int size); //在main函数里用到,作用是建立棋盘并初始化,
//但从语法角度我不太明白,,等我查明白一定回来更博,,
int unguarded(int col);
int queen_insert(int col);
int queen_remove(int col);
int is_solved();
int print();
int board_size;
private:
bool queen_square[MAXSIZE][MAXSIZE];
int counts; //当前操作行的标号(范围:0 ~ board_size-1 )
//我喜欢称它为 操作光标
int num_of_solution;
};
Queens:初始化棋盘。
Queens::Queens(int size){
board_size=size;
counts=0;
num_of_solution=0;
for(int row=0;row<board_size;row++)
for(int col=0;col<board_size;col++)
queen_square[row][col]=not_occupied;
}
unguarded:检查当前位置是否可以放皇后。
Queens::unguarded(int col){
int i;
bool ok=true;
for(i=0;ok&&i<counts;i++)//检验这一列,不要忘记&&ok,否则可能会覆盖掉false
ok=!queen_square[i][col];
for(i=1;ok&&counts-i>=0&&col-i>=0;i++)//检验左上角
ok=!queen_square[counts-i][col-i];
for(i=1;ok&&counts-i>=0&&col+i<board_size;i++)//检验右上角
ok=!queen_square[counts-i][col+i];
return ok;
}
is_solved:判断问题是否解决了(因为行的范围是 0 ~ board_size-1,所以当board_size-1行放完皇后,操作光标指向board_size行,问题就解决了)
Queens::is_solved(){
if(counts==board_size)
return true;
else
return false;
}
print:打印棋盘。
Queens::print(){
num_of_solution++;
cout<<"THE "<<num_of_solution<<" ANSWER:"<<endl<<endl;
for(int i=0;i<board_size;i++)
{
for(int j=0;j<board_size;j++)
{
if(queen_square[i][j])
cout<<" O ";
else cout<<" + ";
}
cout<<endl<<endl;
}
cout <<endl<<endl;
}
queen_insert:放皇后,把操作光标更新到下一行。
Queens::queen_insert(int col){
queen_square[counts][col]=occupied;
counts++;
}
queen_remove:让操作光标从下一行回来,并移除皇后。
Queens::queen_remove(int col){
counts--;
queen_square[counts][col]=not_occupied;
}
solve_from:递归函数,因为要对棋盘本身做改动,所以传参用到了引用。
void solve_from(Queens &configuration){
if(configuration.is_solved())
configuration.print();
else
{
for(int col=0;col<configuration.board_size;col++)
if(configuration.unguarded(col))
{
configuration.queen_insert(col);
solve_from(configuration);
configuration.queen_remove(col);
}
}
}
主函数:
int main(){
int board_size;
cout<<"What's the size of the board?"<<endl
<<"The size should between 1 and 20."<<endl;
cin>>board_size;
while(board_size>MAXSIZE||board_size<1)
{
cout<<"Please input a valid size!"<<endl;
cin>>board_size;
}
Queens configuration(board_size);
solve_from(configuration);
return 0;
}
用四个一维数组代替原来的二维数组
#include<iostream>
#define MAXSIZE 20
#define not_occupied 0
#define occupied 1
using namespace std;
class Queens{
public:
Queens(int size);
int unguarded(int col);//检查是否可以放皇后
int queen_insert(int col);//放皇后
int queen_remove(int col);//移除皇后
int is_solved();//
int print();//
int board_size;//
private:
int queen_in_col[MAXSIZE];//
bool col_free[MAXSIZE];
bool left_upward_free[2*MAXSIZE-1];
bool right_upward_free[2*MAXSIZE-1];
int counts;//
int num_of_solution;//
};
Queens::Queens(int the_size){ //初始化棋盘
board_size=the_size;
counts=0;
num_of_solution=0;
for(int row=0;row<board_size;row++)
queen_in_col[row]=0;
for(int i=0;i<board_size;i++)
col_free[i]=true;
for(int i=0;i<2*board_size-1;i++)
left_upward_free[i]=true;
for(int i=0;i<2*board_size-1;i++)
right_upward_free[i]=true;
}
Queens::unguarded(int col){
int i;
bool ok=true;
if(ok&&col_free[col]==false)
ok=false;
if(ok&&left_upward_free[counts-col+board_size-1]==false)
ok=false;
if(ok&&right_upward_free[counts+col]==false)
ok=false;
return ok;
}
Queens::is_solved(){
if(counts==board_size)
return true;
else
return false;
}
Queens::print(){
num_of_solution++;
cout<<"THE "<<num_of_solution<<" ANSWER:"<<endl<<endl;
for(int row=0;row<board_size;row++)
{
for(int col=0;col<board_size;col++)
{
if(col==queen_in_col[row])
cout<<" O ";
else cout<<" + ";
}
cout<<endl<<endl;
}
cout <<endl<<endl;
}
Queens::queen_insert(int col){
queen_in_col[counts]=col;
col_free[col]=false;
left_upward_free[counts-col+board_size-1]=false;
right_upward_free[counts+col]=false;
counts++;
}
Queens::queen_remove(int col){
counts--;
col_free[col]=true;
left_upward_free[counts-col+board_size-1]=true;
right_upward_free[counts+col]=true;
queen_in_col[counts]=0;
}
void solve_from(Queens &configuration){
if(configuration.is_solved())
configuration.print();
else
{
for(int col=0;col<configuration.board_size;col++)
if(configuration.unguarded(col))
{
configuration.queen_insert(col);
solve_from(configuration);
configuration.queen_remove(col);
}
}
}
int main(){
int board_size;
cout<<"What's the size of the board?"<<endl
<<"The size should between 1 and 20."<<endl;
cin>>board_size;
while(board_size>MAXSIZE||board_size<1)
{
cout<<"Please input a valid size!"<<endl;
cin>>board_size;
}
Queens configuration(board_size);
solve_from(configuration);
return 0;
}