zoukankan      html  css  js  c++  java
  • 回溯算法

    回溯的描述:

    回溯算法将解空间看作一定的结构,通常为树形结构,一个解对应于树中的一片树叶。算法从树根(即初始状态出发),尝试所有可能到达的结点。当不能前行时就后退一步或若干步,再从另一个结点开始继续搜索,直到尝试完所有的结点。也可以用走迷宫的方式去理解回溯,设想把你放在一个迷宫里,想要走出迷宫,最直接的办法是什么呢?没错,试。先选一条路走起,走不通就往回退尝试别的路,走不通继续往回退,直到走遍所有的路,并且在走的过程中你可以记录所有能走出迷宫的路线。

    回溯与八皇后问题

    在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行、列和斜线的敌方棋子。八皇后问题是这样一个问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法?

    如果使用暴力算法的话要在8*8个格子中选择8个格子来摆放皇后,一共要尝试C864次,十亿级别的尝试次数!

    稍加分析,我们可以得到一个不那么暴力的办法,显然,每行每列最多只能有一个皇后,如果基于这个事实进行暴力破解,那结果会好得多。安排皇后时,第一行有8种选法,一旦第一行选定,假设选为(1,i),第二行只能选(2,j),其中j!=i,所以有7种选法。以此类推,需要穷举的情况有8!=40320种。

    我们先降下规模来看看4皇后问题:4个皇后在4*4的格子里各自安排不打架,一共有多少种安排方法?

    试着来穷举一下,真的需要4!=24次尝试吗?现在我们把第一个皇后放在第一个格子,被涂黑的地方是不能放皇后的。

    第二行的皇后只能放在第三格或第四格,按照回溯我们要先尝试放在第三格,则:

    这样第三位皇后无论放哪里都难逃被吃掉的厄运。于是在第一个皇后位于1号,第二个皇后位于3号的情况下问题无解。我们只能返回上一步来,给2号皇后换个位置。

    显然,第三个皇后只有一个位置可选。当第三个皇后占据第三行蓝色空位时,第四行皇后无路可走,于是发生错误,返回上层调用(3号皇后),而3号也别无可去,继续返回上层调用(2号),2号已然无路可去,继续返回上层(1号),于是1号皇后改变位置如下,继续搜索。

    话说到这里,我们对“回溯法”已经有了基本概念。然而所谓知易行难,理解算法和将算法写出来完全是两回事。下面来看看代码:

    代码不长,注释很多

    #include <iostream>
    using namespace std;
    
    bool isOk(int c[], int row, int n); //函数:判断能否在第row行第c[row]列插入一个皇后
    void queen(int row, int c[], int n, int& total);    //函数:回溯的核心部分
    
    int main()
    {
        int n;  //皇后的数量
        cout << "Please enter the number of queen:
    ";
        cin >> n;
        int* c = new int[n];    //记录皇后的位置,例如c[i]的值为j表示第i行的皇后放在第j列
        int total = 0;  //解决方案的种数
        queen(0, c, n, total);
        cout << "The number of solution is:
    " << total;
        return 0;
    }
    
    void queen(int row, int c[], int n, int& total)
    {
        if (row == n)   //当row等于n的时候表示皇后全部摆放完毕
        {
            for (int i = 0; i < n; i++)
                cout << c[i] << " ";
            cout << endl;
            total++;
        }
        else    //皇后还未摆放完,则执行下面程序
        {
            //遍历所有的列,看看第row行皇后可以放在第几列
            for (int col = 0; col < n; col++)   
            {
                c[row] = col;
                //如果可以放在row行col列则继续摆放下一行
                if (isOk(c, row, n))    
                    queen(row+1, c, n, total);
                //如果不可以放在row行col列则试试下一列
            }
            //如果循环了所有的列都不能摆放,则会回溯到前一层函数改变上一行皇后的摆放
        }
    }
    
    bool isOk(int c[], int row, int n)
    {
        for (int i = 0; i < row; i++)
        {   //第row行皇后不能和任意之前的皇后在同一列或 方向或 / 方向
            if (c[i] == c[row] || c[row]-row == c[i]-i || c[row]+row == c[i]+i)
                return false;
        }
        return true;
    }

     算法是逐行安排皇后的,其参数row为现在正执行到第几行(row是从0开始的,0表示第一行)。n是皇后数。

    参考博客:https://www.cnblogs.com/bigmoyan/p/4521683.html

  • 相关阅读:
    [转]HD钱包的助记词与密钥生成原理
    [转]简单科普私钥、地址、助记词、Keystore的区别
    [转]Sequelize 中文API文档-4. 查询与原始查询
    [转]Node.JS使用Sequelize操作MySQL
    [转]OmniLayer / omnicore API 中文版
    [转]usdt omnicore testnet 测试网络
    [转]USDT与omniCore钱包
    [转]BTC RPC API GetTransaction
    [转]比特币测试链——Testnet介绍
    [转]BTC手续费计算,如何设置手续费
  • 原文地址:https://www.cnblogs.com/huwt/p/10749234.html
Copyright © 2011-2022 走看看