递归回溯 N皇后问题 四种写法详解(C语言/C++——一维数组/二维数组)

N皇后问题,做之前感觉挺难,但做着做着发现其实还挺简单的。用递归写的代码简直不要太简洁!

令N=8,效果如图:
递归回溯 N皇后问题 四种写法详解(C语言/C++——一维数组/二维数组)_第1张图片

在这里我列出了四种解法,具体讲解见注释:
C语言解法
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;
}

C++

#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;
}

一维数组解法

C++

用四个一维数组代替原来的二维数组

#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;
}

你可能感兴趣的:(大一下,数据结构,数据结构课内作业)