经典算法问题-01-八皇后

八皇后问题

问题描述:

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

简化问题:

由于八皇后问题的正确答案为92种排列方案,由于正确的棋子放置方式实在太多,难以一一列举,所以先简化问题,解决四皇后的排列,即将8×8棋局改为4×4棋局,规则不变

建立模型:

要使用C++解决这个问题,首先要建立模型,让问题能够使用数学和C++的控制语句来解决。
将棋盘模拟为一个坐标轴,使用一个数组index将每个棋子定位到坐标轴中,index数组的下标为横坐标值,index[i]的值为纵坐标值,如下图所示,index[0] = 1 ; index[1] = 4 ; index[2] = 2 ; index[3] = 3

《经典算法问题-01-八皇后》

方案1—-枚举法:

将所有可能的棋子放置情况全部列举出来,一一判断是否合乎条件

C++程序:

#include<iostream>
using namespace std;

//判断棋子放置方式是否正确
bool Judge(int *index, int i) {
    for (int i = 0; i < 4; i++){
        for (int j = i + 1 ; j < 4; j++) {
            //判断棋子是否在同一行,或某一对角线上
            //1,横坐标不同 i与j不相等
            //2,纵坐标不同 index[i]与index[j]不相等
            //3,不在同一对角线 index[i]- index[j]的绝对值和j-i的绝对值不相等
            if (index[i] == index[j] || abs(index[i]- index[j])==j-i){
                return false;
            }
        }
    }
    return true;
}

//打印正确的棋子放置方式
void Sloution(int *index) {
    cout << "四皇后问题的一种正确解法,横坐标从0到4的4列上,纵坐标的值依次是:";
    for (int i = 0; i < 4; i++){
        cout << index[i];
    }
    cout << endl;
}

int main() {
    int index[8];
    //多层循环,穷举每一种放置方式
    for (index[0] = 1; index[0] <= 4 ; index[0]++){
        for (index[1] = 1; index[1] <= 4; index[1]++) {
            for (index[2] = 1; index[2] <= 4; index[2]++) {
                for (index[3] = 1; index[3] <= 4; index[3]++) {
                    //判断棋子放置是否合乎规则,正确则打印,不正确则继续
                    if (Judge(index,4)){
                        Sloution(index);
                    }
                    else {
                        continue;
                    }
                }
            }
        }
    }

    cin.get();
    return 0;
 }

上述程序使用了4个for循环来模拟所有可能出现的棋子放置情况,Judge()方法用来判断是否合乎条件

运行结果:

《经典算法问题-01-八皇后》

正确结果的演示:

纵坐标:2413

《经典算法问题-01-八皇后》

纵坐标:3142

《经典算法问题-01-八皇后》

枚举法的优化:

枚举法解决此四皇后问题需要的循环次数太多,四皇后需要4X4X4X4次遍历,才能将所有可能的情况列举出来,八皇后更是要8X8X8X8X8X8X8X8次遍历
枚举法可以进行优化,即若在放置第2,或3个棋子,即没有将棋子全部放入棋盘中时,进行判断,将不符合要求的情况直接排除,不再继续循环,这样能够大大减少循环次数,不必模拟所有棋子放置方式
优化方式:在main方法中做优化,增加判断语句

int main() {
    int index[8];
    //多层循环,穷举每一种放置方式
    for (index[0] = 1; index[0] <= 4; index[0]++) {
        for (index[1] = 1; index[1] <= 4; index[1]++) {
            //增加判断
            if(Judge(index, 2))
                for (index[2] = 1; index[2] <= 4; index[2]++) {
                    //增加判断
                    if (Judge(index, 3))
                        for (index[3] = 1; index[3] <= 4; index[3]++) {
                            //判断棋子放置是否合乎规则,正确则打印,不正确则继续
                            if (Judge(index, 4)) {
                                Sloution(index);
                            }
                            else {
                                continue;
                            }
                        }
                }
        }
    }

    cin.get();
    return 0;
}

方案2—-回溯法:

main函数:


int main() {

    int index[4] = { 0 };
    int i = 0;
    while (i > -1) {
        index[i]++;
        while (index[i] < 6 && _judge(index, i)) {
            index[i]++;
        }

            if (index[i] <= 4) {

                if (i == 3) {
                    //是四皇后的正确结果,调用函数打印正确答案
                    result(index);
                }
                else {
                    i++;
                    index[i] = 0;
                }
            }
            else {
                i--;
            }

    }

    cin.get();
    return 0;
}

_judge函数:

inline bool _judge(int *index, int num) {

    for (int i = 0; i < num; i++) {
        if (index[i] == index[num] || abs(index[i] - index[num]) == num - i) {
            return true;
        }
    }
    return false;

}

result函数:

inline void result(int *index) {
    cout << "四皇后问题的一种正确解法,横坐标从0到4的4列上,纵坐标的值依次是:";
    for (int i = 0; i < 4; i++) {
        cout << index[i];
    }
    cout << endl;
}

《经典算法问题-01-八皇后》

使用递归优化程序:

上述程序太难懂,可以使用递归优化上述程序,如下:
result函数和_judge函数不变
回调函数:

void callback(int *index,int i) {
    if (i > 3){
        result(index);
    }
    else{
        int j = 0;
        while (++j <= 4) {
            index[i] = j;
            if (_judge(index, i) == 0) {
                callback(index,i+1);
            }
        }

    }
}

main函数:

int main() {
    int index[4] = { 0,0,0,0 };
    int i = 0;
    callback(index,i);
    cin.get();
    return 0;
}

方案3—-回溯法结合递归和栈数据结构

在严蔚敏编著的C语言数据结构栈数据结构部分,书中提及使用栈解决四皇后问题

《经典算法问题-01-八皇后》

栈数据结构解决和回溯相关的问题时非常的方便易懂,下面介绍一下使用栈解决四皇后问题,这是本文最简单易懂的方法,但是需要实现编写栈数据结构,当然也可以使用STL中的栈

#include "assist.h"
#include"Stack.h"
#include<iostream>
//创建一个全局栈
Stack<int> stack(11);
//回调函数
void callback() {
    if (stack.getSize() == 3) {
        cout << "正确答案" << endl;
        stack.Print();
    }
    else {
        int j = 0;
        while (++j <= 4)
        {
            //进栈
            stack.Push(j);
            if (_judge( stack.getElements(), stack.getSize()) == 0) {
                //打印整个栈中的数据
                stack.Print();
                cout << stack.getSize() << endl;
                callback();
            }
            //弹栈
            stack.Pop();
        }

    }
}


int main() {

    callback();

    cin.get();
    return 0;
}

《经典算法问题-01-八皇后》

    原文作者:八皇后问题
    原文地址: https://blog.csdn.net/eagleuniversityeye/article/details/78565740
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞