• 八皇后问题—经典回溯算法


    八皇后问题

    八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。

    回溯算法思想

    回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。

    八皇后实现二

    以下实现是极客时间王争的解法,非常巧妙,思路也非常清晰,如果理解了八皇后问题的本质后建议采用该方法,代码实现如下:

    #include <iostream>
    
    int queenPlace[8] = { 8 };  //全局变量,下标表示行,值表示queen存储在那一列
    int count = 0;  //计数器
    
    void printQueen() {  //打印一个二维数组
      for (int i = 0; i < 8; ++i) {
        for (int j = 0; j < 8; ++j) {
          if (queenPlace[i] == j) {
            printf("Q ");
          } else {
            printf("* ");
          }
        }
        printf("
    ");
      }
      printf("----count:%d-----
    ", ++count);
    }
    
    bool isOk(int row, int col) {  //判断row行col列放置是否合适
      int leftUp = col - 1;  //左上对角线
      int rightUp = col + 1; //右上对角线
      for (int i = row - 1; i >= 0; --i) { 
        if (queenPlace[i] == col) return false;  //同列上的格子有皇后
        if (leftUp >= 0) {
          if (queenPlace[i] == leftUp) return false;   //左上对角线有皇后
        }
        if (rightUp < 8) {
          if (queenPlace[i] == rightUp) return false;  //右上对角线有皇后
        }
        --leftUp; ++rightUp;
      }
      return true;
    }
    
    void eightQueen(int row) {
      if (row == 8) {  //8个皇后都放置好,打印,无法递归返回
        printQueen();
        return;
      }
      for (int col = 0; col < 8; ++col) {  //每一行都有8种方法
        if (isOk(row, col)) {    //满足要求
          queenPlace[row] = col; //第row行的皇后放在col列
          eightQueen(row+1);     //考察下一行
        }
      }
    }
    
    
    int main() {
        eightQueen(0);return 0;
    }

     LeetCode 51

    class Solution {
    public:
        vector<vector<string>> res;
        vector<int> n_queen;
        vector<vector<string>> solveNQueens(int n) {
            n_queen.resize(n);
            backtrack(0);
            return res;
        }
        void backtrack(int row) {
            if (row == n_queen.size()) {
                storeResult();
                return;
            }
            for (int i = 0; i < n_queen.size(); ++i) {
                if (!isOk(row, i)) continue;
                n_queen[row] = i;
                backtrack(row + 1);
            }
        }
        bool isOk(int row, int col) {
            int left_up = col - 1;
            int right_up = col + 1;
            for (int i = row - 1; i >= 0; --i) {
                if (n_queen[i] == col             // 当前列
                    || n_queen[i] == left_up--      // 左上对角,无需判断 left_up < 0, 该情况不会成立的
                    || n_queen[i] == right_up++) {  // 右上对角,无需判断 right_up > n_queen.size()
                    return false;
                }
            }
            return true;
        }
        void storeResult() {
            vector<string> result;
            for (auto i : n_queen) {
                string s(n_queen.size(), '.');
                s[i] = 'Q';
                result.push_back(s);
            }
            res.push_back(result);
        }
    };

    解法2:

    class Solution {
    public:
        vector<bool> col;
        vector<bool> dia1;     
        vector<bool> dia2;
        vector<vector<string>> result;
        vector<string> generateQueen(vector<int>& q)
        {
            vector<string> res;
            for (int i = 0; i < q.size(); ++i)
            {
                string s(q.size(), '.');
                s[q[i]] = 'Q';
                res.push_back(s);
            }
            return res;
        }
        void traceBack(int n, int row, vector<int>& q)
        {
            if (row == n) {
                result.push_back(generateQueen(q));
                return;
            }
            for (int i = 0; i < n; ++i)
            {
                if (!col[i] && !dia1[row + i] && !dia2[row - i + n - 1])
                {
                    q.push_back(i);
                    col[i] = true;
                    dia1[row + i] = true;
                    dia2[row - i + n - 1] = true;
                    traceBack(n, row + 1, q);
                    col[i] = false;
                    dia1[row + i] = false;
                    dia2[row - i + n - 1] = false;
                    q.pop_back();
                }
            }
        }
        vector<vector<string>> solveNQueens(int n) {
            col = vector<bool>(n, false);
            dia1 = vector<bool>(2 * n - 1, false);
            dia2 = vector<bool>(2 * n - 1, false);
            vector<int> q;
            traceBack(n, 0, q);
            return result;
        }
    };
  • 相关阅读:
    [K/3Cloud] 首页增加一个自定义页签及页面内容
    [K/3Cloud]DBServiceHelper.ExecuteDataSet(this.Context, sql)) 返回数据问题
    [K/3Cloud] 表单python脚本使用QueryService的做法
    [K/3Cloud]有关单据显示行数限制和数据导出的建议
    [K/3Cloud]实现双击列表行后显示具体的某个单据明细。
    [K/3Cloud]K3Cloud的移动审批方面
    [K/3Cloud]K3Cloud的移动审批方面
    [K/3Cloud]关于数据库sa密码更改,管理中心登录不上的问题。
    游戏编程最前沿....
    marmalade android 5.0 JNI 调用失败的解决方案
  • 原文地址:https://www.cnblogs.com/evenleee/p/11946724.html
Copyright © 2020-2023  润新知