C++ 【回溯】经典例题——N皇后

N皇后

题目[leetcode 51]

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

皇后的攻击范围为:

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

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

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

示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],

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

提示:
皇后,是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一到七步,可进可退。(引用自 百度百科 - 皇后 )

思路

这里设计了一张图mark来说明该位置是否能放皇后,如果mark[i][j]==1时,说明在其他的皇后攻击范围,不能下;若 ==0,则能下,那么通过这张图就能准确地试探这样的下棋方式是否可行,若不可行,则回溯到上一个棋子前,改为另一个可下的位置。

修改mark的函数为put_down_the_queen,这个函数利用方向数组的形式将皇后位置上的八个方向进行了遍历,使得后面的皇后不可在这些位置上下棋子。

实现代码如下:

void put_down_the_queen(int x, int y,vector<vector<int>> &mark){
	   //方向数组
       static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
       static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
       mark[x][y] = 1;
       for(int i = 1; i < mark.size(); i++){
           for(int j = 0; j < 8; j++){
               int new_x = x + i * dx[j];
               int new_y = y + i * dy[j];
               if(new_x >= 0 && new_x < mark.size() &&
                   new_y >= 0 && new_y < mark.size())
               { //不超过边界(越界)时
                   mark[new_x][new_y] = 1;
               }
           }
}

然后便是尝试性回溯的函数generate,这个主要是在一行一行的形式去摆放皇后,有以下情况出现:

①如果在该行的某位置(i, j)不在其他皇后的攻击范围上,即mark[i][j] == 0时,便可进行下一行的尝试。

②当这一行上的位置因前面皇后的摆放,均不可放入,则回溯到上一行,改为下一个可放位置,直至N个皇后均放入到棋盘中去。

展现形式如下:

实现代码如下:

void generate(int k, int n,
                  vector<string>& location,
                  vector<vector<string>> &result,
                  vector<vector<int>> &mark){
        if(k == n){ //N个皇后均已放入时,结束递归
            result.push_back(location);
            return;
        }
        for(int i = 0; i < n; i++){ //把该行的n个位置军尝试一遍
            if(mark[k][i] == 0){ //该位置可放皇后时
                vector<vector<int>> temp_mark = mark; //保存未修改时的mark,有误回溯时可用
                location[k][i] = 'Q'; //放皇后
                put_down_the_queen(k, i, mark); //遍历皇后的攻击范围
                generate(k + 1, n, location, result, mark); //进入下一行探索
                mark = temp_mark; //下一行探索失败时,回溯mark
                location[k][i] = '.';//拿回皇后
            }
        }
    }

之后就是数据结构的构建主要函数的调用

实现代码如下:

vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> result;
        vector<vector<int>> mark;
        vector<string> location;
        for(int i = 0; i < n; i++){ //构建起初的mark结构
            mark.push_back((vector<int>()));
            for(int j = 0; j < n; j++){
                mark[i].push_back(0);
            }
            location.push_back("");//棋盘的构造
            location[i].append(n, '.');
        }
        generate(0, n, location, result, mark); //函数调用
        return result;
    }

致谢

本章知识点和思路由小象学院相关视频提供,由本人学习并梳理得出,希望自己加深记忆的同时,也能给大家提供更多有关于一些算法的知识点。
你的点赞、评论、收藏就是对我最大的支持与鼓励,谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值