C++刷题笔记:回溯

N 皇后问题

「N 皇后问题」研究的是如何将 N 个皇后放置在 N ×N 的棋盘上,并且使皇后彼此之间不能相互攻击。皇后的走法是:可以横直斜走,格数不限。因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。

采用回溯法,记录上一步的位置,判断当前位置和上一位置是否在同一行、同一列或同一条斜线上。这一判断需要技巧。
在这里插入图片描述

初始位置:在第 row=0 行中选位置,每次递归 row+1

unordered_set<int> columns 记录以往位置的列标。

研究发现,同一条左斜线的行列索引和相等,同一右斜线的行列索引差相等,是每条斜线的特性。因此可以用 unordered_set<int> left_diag, right_diag 记录以往的位置信息。

class Solution {
public:
    vector<vector<string>> results;
    vector<int> track;
    unordered_set<int> columns, left_diag, right_diag;

    vector<vector<string>> solveNQueens(int n) {
        trackback(0, n);
        return results;
    }

    void trackback(int row, int n) {
        if (row == n) {
            auto r = createAnswer(track, n);
            results.push_back(move(r));
        } else {
            for (int i = 0; i < n; i++) {
                // 边界检查
                if (columns.count(i)
                    || left_diag.count(i + row)
                    || right_diag.count(i - row))
                    continue;

                // 做选择
                track.push_back(i);
                columns.insert(i);
                left_diag.insert(i + row);
                right_diag.insert(i - row);
                // 前进
                trackback(row + 1, n);
                // 回溯(后退)
                track.pop_back();
                columns.erase(i);
                left_diag.erase(i + row);
                right_diag.erase(i - row);
            }
        }
    }

    vector<string> createAnswer(vector<int> &queens, int n) {
        vector<string> ret(n, string(n, '.'));
        for (int i = 0; i < n; i++) {
            int index = queens[i];
            ret[i][index] = 'Q';
        }
        return ret;
    }
};

357. 计算各位数字互不相同的n位数有多少个

n = 0,有1个
n = 1,有10个
n = 2,有91个

这里不用排列组合的方法,只讨论回溯法。

路径中记录的类似这种 0 -> 0 -> 1 -> 2 也应该是符合条件的,需要特殊处理。

class Solution {
public:
    vector<int> track;
    int ret = 0;

    int countNumbersWithUniqueDigits(int n) {
        if (n == 0) return 1;
        backtrack(0, n);
        return ret;
    }

    void backtrack(int index, int n) {
        if (index == n) {
            ret++;
        } else {
            for (int i = 0; i < 10; i++) {
                // 检查
                if (find(track.begin(), track.end(), i) != track.end()) {  // 数字重复了,非0数直接排除
                    if ((i == 0 && track.back() != 0) || i != 0)  // 数字如果是0,且上一个数字不是0再排除
                        continue;
                }
                // 做选择
                track.push_back(i);
                // 前进
                backtrack(index + 1, n);
                // 撤回
                track.pop_back();
            }
        }
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值