剪枝?我直接刷爆

csdn

剪枝(Pruning algorithm)

数据结构简介…


高频面试题

1.力扣第51和52题 - N皇后

🚀题目链接:LeetCode51.N 皇后
🚀题目链接:LeetCode52.N皇后 II

力扣第51题要找到所有可行解,第52题只需要返回可行解的数量。第51题要难一些,这里就以第51题为例,给出 N皇后 问题的代码,第52题只需要在此基础上稍加修改即可。

题目:

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

示例 1:
1

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

🍬C++ AC代码:

class Solution {
private:
    vector<vector<string>> res;
    //  使用set保存每个皇后可以攻击到的位置
    unordered_set<int> col, left_slash, right_slash;

public:
    vector<vector<string>> solveNQueens(int n) {
    	//  初始化棋盘
        char** board = new char*[n];
        for (int i = 0; i < n; i++)
            board[i] = new char[n];
        for (int i = 0; i < n; i++)
            memset(board[i], '.', n * sizeof(char));
        dfs(board, 0, n);
        return res;
    }

    void dfs(char** board, int row, int n) {
    	//  棋盘的所有行都访问完,说明找到了一个可行方案
        if (row == n) {
        	//  将当前棋盘放入结果集中
            res.push_back(generate_result(board, n));
            return;
        }
        //  枚举当前行上的所有位置,如果没有其他皇后攻击则放置一个皇后
        for (int i = 0; i < n; i++) {
            if (col.find(i) == col.end() && left_slash.find(row + i) == left_slash.end() && right_slash.find(row - i) == right_slash.end()) {
                board[row][i] = 'Q';
                col.insert(i);
                left_slash.insert(row + i);
                right_slash.insert(row - i);
                //  访问棋盘的下一行
                dfs(board, row + 1, n);
                //  回溯,继续搜索其他可行方案
                board[row][i] = '.';
                col.erase(i);
                left_slash.erase(row + i);
                right_slash.erase(row - i);
            }
        }
    }

	//  根据当前棋盘状态生成题目要求的返回结果
    vector<string> generate_result(char** board, int n) {
        vector<string> temp_res;
        for (int i = 0; i < n; i++) {
            string row = "";
            for (int j = 0; j < n; j++)
                row += board[i][j];
            temp_res.push_back(row);
        }
        return temp_res;
    }
};

Tips:

  • char**是二级指针,用来指向一个二维数组,由于C++使用数组作为参数必须声明大小,因此这里用指针代替:dfs(char** board, int row, int n)

Java AC代码:

class Solution {

    List<List<String>> res = new ArrayList<>();
    //  使用set保存每个皇后可以攻击到的位置
    Set<Integer> col = new HashSet<>();
    Set<Integer> leftSlash = new HashSet<>();
    Set<Integer> rightSlash = new HashSet<>();

    public List<List<String>> solveNQueens(int n) {
    	//  初始化棋盘
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(board[i], '.');
        dfs(board, 0, n);
        return res;
    }

    public void dfs(char[][] board, int row, int n) {
    	//  棋盘的所有行都访问完,说明找到了一个可行方案
        if (row == n) {
        	//  将当前棋盘放入结果集中
            res.add(generateResult(board, n));
            return;
        }
        //  枚举当前行上的所有位置,如果没有其他皇后攻击则放置一个皇后
        for (int i = 0; i < n; i++) {
            if (!col.contains(i) && !leftSlash.contains(row + i) && !rightSlash.contains(row - i)) {
                board[row][i] = 'Q';
                col.add(i);
                leftSlash.add(row + i);
                rightSlash.add(row - i);
                //  访问棋盘的下一行
                dfs(board, row + 1, n);
                //  回溯,继续搜索其他可行方案
                board[row][i] = '.';
                col.remove(i);
                leftSlash.remove(row + i);
                rightSlash.remove(row - i);
            }
        }
    }

	//  根据当前棋盘状态生成题目要求的返回结果
    public List<String> generateResult(char[][] board, int n) {
        List<String> tempRes = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            StringBuilder row = new StringBuilder("");
            for (int j = 0; j < n; j++)
                row.append(board[i][j]);
            tempRes.add(row.toString());
        }
        return tempRes;
    }
}

🍦Python AC代码:

class Solution(object):
    def solveNQueens(self, n):
        if n < 1: return []
        self.result = []
        #  使用set保存每个皇后可以攻击到的位置
        self.cols = set(); self.slash_l = set(); self.slash_r = set()
        self.DFS(n, 0, [])
        return self._generate_result(n)

    def DFS(self, n, row, cur_state):
    	#  棋盘的所有行都访问完,说明找到了一个可行方案
        if row >= n:
            self.result.append(cur_state)
            return
        #  枚举当前行上的所有位置,如果没有其他皇后攻击则放置一个皇后
        for col in range(n):
            if col in self.cols or row + col in self.slash_l or row - col in self.slash_r:
                continue
            self.cols.add(col)
            self.slash_l.add(row + col)
            self.slash_r.add(row - col)
            #  访问棋盘的下一行
            self.DFS(n, row + 1, cur_state + [col])
            #  回溯,继续搜索其他可行方案
            self.cols.remove(col)
            self.slash_l.remove(row + col)
            self.slash_r.remove(row - col)

	#  根据当前棋盘状态生成题目要求的返回结果
    def _generate_result(self, n):
        board = []
        for res in self.result:
            for i in res:
                board.append("." * i + "Q" + "." * (n - i - 1))
        return [board[i: i + n] for i in range(0, len(board), n)]

Tips:

  • ⭐Python的代码中没有初始化一个棋盘,而是直接将棋盘作为参数,在递归访问棋盘的每一行时动态生成:DFS(n, row + 1, cur_state + [col])

2.力扣第36和37题 - 数独

🚀题目链接:LeetCode36.有效的数独
🚀题目链接:LeetCode37.解数独

力扣第36题给我们一个数独,让我们来判断已经填入的数字是否有效,第37题让我们找到数独的可行方案。第37题要难一些,这里只给出37题的参考代码。

题目:

编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
    数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:
1

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."]
[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"]
["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"]
[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"]
["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"]
["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"]
["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"]
["3","4","5","2","8","6","1","7","9"]]

解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
2
题目数据 保证 输入数独仅有一个解。

🍬C++ AC代码:

class Solution {
private:
    // 标记行,列,与3*3的方块中已经出现的数字
    bool rows[9][10];
    bool cols[9][10];
    bool block[3][3][10];
    // 创建一个额外的数组保存"."出现的位置
    vector<pair<int, int>> dic;
    // 创建一个辅助数组保存答案
    vector<vector<char>> res;

public:
    void solveSudoku(vector<vector<char>>& board) {
        // 预处理每行每列以及3*3方块中已经出现的数字
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    rows[i][board[i][j] - '0'] = true;
                    cols[j][board[i][j] - '0'] = true;
                    block[i / 3][j / 3][board[i][j] - '0'] = true;
                } else {
                    // 保存待填入数字的位置
                    dic.push_back({i, j});
                }
            }
        }
        dfs(board, 0);
        board = res;
    }

    void dfs(vector<vector<char>>& board, int pos) {
        // 所有待填入位置已经访问完,递归结束
        if (pos == dic.size()) {
            // 在回溯之前将当前的矩阵保存起来,防止回溯时填入的值被打乱
            res = board;
            return;
        }
        int x = dic[pos].first, y = dic[pos].second;
        // 枚举1~9,尝试填入当前位置
        for (int i = 1; i <= 9; i++) {
            // 检查是否满足条件
            if (!rows[x][i] && !cols[y][i] && !block[x / 3][y / 3][i]) {
                rows[x][i] = true;
                cols[y][i] = true;
                block[x / 3][y / 3][i] = true;
                // 填入满足条件的数字
                board[x][y] = i + '0';
                // 递归,访问下一个位置
                dfs(board, pos + 1);
                // 回溯
                rows[x][i] = false;
                cols[y][i] = false;
                block[x / 3][y / 3][i] = false;
            }
        }
    }
};

Java AC代码:

class Solution {

	// 标记行,列,与3*3的方块中已经出现的数字
    boolean[][] rows = new boolean[9][10];
    boolean[][] cols = new boolean[9][10];
    boolean[][][] block = new boolean[3][3][10];
    // 创建一个额外的数组保存"."出现的位置
    List<int[]> dic = new ArrayList<int[]>();
    // 创建一个辅助数组保存答案
    char[][] res = new char[9][9];

    public void solveSudoku(char[][] board) {
    	// 预处理每行每列以及3*3方块中已经出现的数字
        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] != '.') {
                    int digit = board[i][j] - '0';
                    rows[i][digit] = cols[j][digit] = block[i / 3][j / 3][digit] = true;
                } else {
                	// 保存待填入数字的位置
                    dic.add(new int[]{i, j});
                }
            }
        }
        dfs(board, 0);
        for (int i = 0; i < 9; i++) {
            board[i] = Arrays.copyOf(res[i], 9);
        }
    }

    public void dfs(char[][] board, int pos) {
    	// 所有待填入位置已经访问完,递归结束
        if (pos == dic.size()) {
        	// 在回溯之前将当前的矩阵保存起来,防止回溯时填入的值被打乱
            for (int i = 0; i < 9; i++) {
                res[i] = Arrays.copyOf(board[i], 9);
            }
            return;
        }
        int x = dic.get(pos)[0], y = dic.get(pos)[1];
        // 枚举1~9,尝试填入当前位置
        for (int i = 1; i <= 9; i++) {
			// 检查是否满足条件
            if (!rows[x][i] && !cols[y][i] && !block[x / 3][y / 3][i]) {
                rows[x][i] = true;
                cols[y][i] = true;
                block[x / 3][y / 3][i] = true;
                // 填入满足条件的数字
                board[x][y] = (char)(i + '0');
                // 递归,访问下一个位置
                dfs(board, pos + 1);
                // 回溯
                rows[x][i] = false;
                cols[y][i] = false;
                block[x / 3][y / 3][i] = false;
            }
        }
    }
}

Tips:

  • ⭐当递归到出口时需要将当前的数组board保存在答案数组res中,这里不能直接让res = board,原因是Java中两个数组的引用直接赋值时浅拷贝,只会改变引用指向的地址而里面的内容是不会变的。

🍦Python AC代码:

class Solution(object):
    def solveSudoku(self, board):
    	# 标记行,列,与3*3的方块中已经出现的数字
        self.rows = [[False] * 9 for _ in range(9)]
        self.cols = [[False] * 9 for _ in range(9)]
        self.block = [[[False] * 9 for _a in range(3)] for _b in range(3)]
        # dic用来保存"."出现的位置
        self.dic = list()
        # 创建一个辅助数组保存答案
        self.res = []
        # 预处理每行每列以及3*3方块中已经出现的数字
        for i in range(9):
            for j in range(9):
            	# 保存待填入数字的位置
                if board[i][j] == ".":
                    self.dic.append((i, j))
                else:
                    digit = int(board[i][j]) - 1
                    self.rows[i][digit] = self.cols[j][digit] = self.block[i / 3][j / 3][digit] = True
        self.dfs(board, 0)
        for i in range (9):
            board[i] = self.res[i]

    def dfs(self, board, pos):
    	# 所有待填入位置已经访问完,递归结束
        if pos == len(self.dic):
        	# 在回溯之前将当前的矩阵保存起来,防止回溯时填入的值被打乱
            for i in range (9):
                self.res.append(board[i][:])
            return
        i, j = self.dic[pos]
        # 枚举1~9,尝试填入当前位置
        for digit in range(9):
        	# 检查是否满足条件
            if self.rows[i][digit] == self.cols[j][digit] == self.block[i / 3][j / 3][digit] == False:
            	self.rows[i][digit] = self.cols[j][digit] = self.block[i / 3][j / 3][digit] = True
            	# 填入满足条件的数字
                board[i][j] = str(digit + 1)
                # 递归,访问下一个待填入位置
                self.dfs(board, pos + 1)
                # 回溯
                self.rows[i][digit] = self.cols[j][digit] = self.block[i / 3][j / 3][digit] = False

Tips:

  • ⭐当递归到出口时需要将当前的数组board保存在答案数组res中,这里不能直接让res = board,原因是Python中两个数组的引用直接赋值时浅拷贝,只会改变引用指向的地址而里面的内容是不会变的。

总结

┊我们还有更长的路要走,不过没关系,道路就是生活┊
-凯鲁亚克《在路上》 ​​​-

footer

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值