LEECODE [N皇后] [n-queens]

题目描述

皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。


上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

思路

  1. 根据题目的要求,每行、每列有且只有一个皇后,我们可以按照列的方式依次填入皇后,并保证新填入的皇后和已填入的所有皇后不会相互攻击
  2. 选择stack数据结构存储合理的皇后位置,stack最底部的位置存储的是第一列的元素,越往上存储的元素对应越靠右的列;(应该也可以使用vector存储,在使用过程中发现stack不能遍历,需要反复拷贝)
  3. 根据stack已有的元素,挑选下一列中合理的pos(从上而下);如果能找到,则放进stack中,并重复步骤3;
  4. 当找到的元数有n个,则发现一种解法;然后弹出stack中一个元数,寻找该列(最后一列)中下一个合理的pos;
  5. 如果不能找到,则弹出stack中一个元素,并根据弹出元素的位置(至上而下)寻找该列中下一个合理的pos

代码分析

首先定义二维数组中元数的结构,表示位置信息

    struct position {
        int _r;
        int _l;
        position() {
            _r = 0;
            _l = 0;
        }
        position(int r, int l) {
            _r = r;
            _l = l;
        }
    };

下面是我们的主函数

    vector<vector<string>> solveNQueens(int n) {
        _dimension = n;
        vector<vector<string>> ret;
        int row = n;
        int line = n;
        // each row has only one queen
        // each line has only one queen
        stack<position> s;
        int next_row_start=0;
        while(true) {
            position next_pos = get_next_line_valid(s, next_row_start);  // not conflict with all element now
            if(!is_valid(next_pos)) {  // can not find valid element
                if(s.empty()) {
                    break;
                }
                next_row_start = s.top()._r+1; // move to next row in the same line
                s.pop();
                continue;
            }
            // valid element
            s.push(next_pos);
            next_row_start = 0; // reset 
            if(s.size() == n) { // find one solution
                display(s, ret);
                next_row_start = s.top()._r+1;
                s.pop();
            }
        }
        return ret;
    }
  1. get_next_line_valid是根据已经选出的皇后位置(存放进stack中),挑选下一列中能出现皇后的位置。next_row_start变量的作用是保证每一列皇后的选取是至上而下进行,不会重复选取;
  2. is_valid是判断下一列能否返回一个合理的位置,如果不能,则弹出stack中的一个元数,并记录该元数的位置信息;
  3. 如果能找到一个合理的位置信息,则把它push进stack中;
  4. 如果所有列都选出合理的queue的位置,则调用display函数进行展示;

最后我们来看一下其他函数的实现

    position get_next_line_valid(const stack<position>& s, int next_row_start) {
        position ret{-1, -1}; // invalid
        int line_idx = s.size();
        for(int i=next_row_start; i!=_dimension; i++) { // top down direction
            stack<position> tmp(s);
            position cur{i, line_idx};
            bool is_valid = true;
            while(tmp.size()) {
                if(is_conflict(tmp.top(), cur)) {
                    is_valid = false;
                    break;
                }
                tmp.pop(); 
            }
            if(is_valid) {
                ret = cur;
                break;
            }
        }
        return ret;
    }
    bool is_valid(const position& pos) {
        if(pos._r==-1 || pos._l==-1) return false;
        return true;
    }
    bool is_conflict(const position& pos1, const position& pos2) {
        if(pos1._r==pos2._r || pos1._l==pos2._l) {
            return true;
        }
        int row_diff = (pos1._r-pos2._r > 0) ? pos1._r-pos2._r : pos2._r-pos1._r;
        int line_diff = (pos1._l-pos2._l > 0) ? pos1._l-pos2._l : pos2._l-pos1._l;
        if(row_diff == line_diff) {
            return true;
        }
        return false;
    }
    void display(stack<position> s, vector<vector<string>>& ret) {
        assert(s.size() == _dimension);
        vector<string> cur;
        cur.resize(_dimension);
        while(s.size()) {  // from right to left direction
            const position& p = s.top();
            for(int i=0; i!=_dimension; i++) {  // for each string
                if(p._r == i) { // this is the queen
                    cur.at(i) = string(1, 'Q') + cur.at(i);
                }else {
                    cur.at(i) = string(1, '.') + cur.at(i);
                }
            }
            s.pop();
        }
        ret.push_back(move(cur));
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值