回溯之N皇后问题

问题及要求:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens

解决思路
首先这道题难度还是比较大的,因为一般角度来思考的话你会觉得情况太多而无从下手,所以这道题是需要回溯的思想,回溯就是深搜算法中衍生的一种思想,就是当你一条路走不通时,你需要返回上一级继续寻找其他的路径,这个返回上一级就是回溯思想的核心,其实还和递归有一样的成分在其中。你只需要这点就可以了。
好了,现在回到这道题上,需要在n*n的棋盘上放n个皇后,并且要求皇后之间不能在同一行,同一列,同一斜线上,那么这时可以先在棋盘的(0,0)位置放一个皇后,然后第二行再依次放判断哪个位置可以放一个皇后,如果第二行四个位置都不满足那么就说明已经走不通了,就更不需要接着考虑第三行了,这时就需要返回上一行(第一行),再在第一行的第一列放一个皇后(0,1),再重复上述操作,直到行到第四行,就可以找到一个满足要求的情况。

拿4*4来说满足要求的有两种情况如下图所示:
在这里插入图片描述
在这里插入图片描述
如下图所示红蓝斜线的判断公式不一样,一个是横纵坐标之和相同,一个是横纵坐标之差相同。
在这里插入图片描述

代码实现:

class Solution {
public:
    void dfs(vector<vector<pair<int,int>>>& res,vector<pair<int,int>>& sol,int currow,int n){
        if(currow==n){//所有行皇后的位置都已确定,保存这种解决方案,能走完最后一行说明之前都是满足的
            res.push_back(sol);
        }
        for(int i=0;i<n;i++){ //列
             if(isvalid(sol,currow,i)){         //此位置放一个皇后会不会和之前存的坐标相冲突
                 //如果不冲突保存当前坐标
                sol.push_back(make_pair(currow,i));  //solution里存的只是当前满足的坐标
                dfs(res,sol,currow+1,n);       //递归下一行
                sol.pop_back();                //走到这里证明当前行不满足
            }
        }                  //因为如果一行中的每列的位置都不满足,那么就会返回到上一行,这是for循环决定的
    }
    
    bool isvalid(vector<pair<int,int>>& sol,int currow,int col){
        //不在同一列,不在同一斜线上
        for(auto& e: sol){
            if(e.second == col||e.first+e.second==currow+col||e.first-e.second==currow-col){   //这个式子你只需要看在一列上的或者在一斜线上的点的坐标关系
                return false;
            }
        }
        return true;
    }
    
    vector<vector<string>> solveNQueens(int n) {
        //判断是否在同一斜线上,横纵坐标和一样或者横纵坐标差一样
        vector<vector<string>> ret;         //用来保存最终结果,如题要求的那样
        vector<vector<pair<int,int>>> res;  //用来保存最终满足的所有坐标组,例如上面的4*4就有两组坐标集合
        vector<pair<int,int>> sol;          //用来保存当前的可能坐标
        dfs(res,sol,0,n);                            
        for(auto& e:res){                   
            vector<string> retstr(n,string(n,'.'));  //针对每个可能的坐标组初始化要求如题
            for(auto& pos: e){
                retstr[pos.first][pos.second] = 'Q';  //遍历坐标组中满足的坐标来把retstr中的元素变为字符Q,一个Q就代表一个皇后
            }
            ret.push_back(retstr);            
        }
        return ret;                  //返回所有可能的集合
    }
};

总结:
在这里运用c++里的pair就很好的解决了存储以及处理坐标的问题,pair的使用频率还是很高的,尤其是遇到键值对的问题,所以合理使用会让程序变得很简单。回溯的核心就是要找到什么时候可以回退到上一层,找到了的话解决问题也就会变得简单。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值