LeetCode back Thematic 8: backtracking is the foundation of classical artificial intelligence

Backtracking is the foundation of classical artificial intelligence, this sentence "classic" can be understood as "traditional." Now, the field of artificial intelligence, there is a very popular topic, and that is machine learning.

Here we have to introduce a traditional artificial intelligence problem: n queens problem. As such, it is a typical recursive backtracking problem.

Example: LeetCode question 51: N Queens

Portal: English Website: 51. The N-Queens , Chinese Web site: 51. The N Queens .

n research queens problem is how to n queens placed n × n on the board, and the queen can not attack each other to each other.

414598-4bc3215dfbd73311.png
LeetCode question 51: N Queens

A Method for Solving the picture shows the 8 queens problem.

Given an integer n , returns all the different n solutions to the problem of the Queen.

Each solution contains an explicit n queens problem pieces placed embodiment, the program 'Q'and '.'represent the space and queen.

Example:

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

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

Analysis: Although it looks, it is a question of artificial intelligence, but our code implementation can be interpreted as violent solution, but we use the "recursive backtracking" mode violent solution, you can quickly determine the course of violence generated the result conditions, not all the violence Solutions are out later remove ineligible;
for example: we in column 1, line 1 has been placed elements, then obviously, row 2, column 1 the first two cases and placement of elements had been ruled out, the first three in the second row we find elements can be placed, then continue traversing down;

From the above analysis, we can see the "recursion", "back" and "violent solution", "depth-first traversal" inextricably linked;

In fact, this road problem, much like the arrangement of the problem we described earlier. We think that method is not the beginning of each layer in fact we have a kind of place, but because the rules of the game, each layer we will rule out some possible state before each layer we will record, back later, the state should recover;

On problem-solving ideas, we used to think the Queen or line by line position placed, so long as the outer layer of the cycle.

Java implementation:

public class Solution {

    private boolean[] col; // 记录在列上第几列已经放置了元素
    private boolean[] dia1; // 记录在主对角线上哪些位置已经放置了元素
    private boolean[] dia2; // 记录在副对角线上哪些位置已经放置了元素
    private List<List<String>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        col = new boolean[n];
        dia1 = new boolean[2 * n - 1]; // 可以用归纳法得到对角线上的元素个数
        dia2 = new boolean[2 * n - 1]; // 可以用归纳法得到对角线上的元素个数
        putQueue(n, 0, new ArrayList<Integer>());
        return res;
    }

    /**
     * 尝试在一个 n 皇后的问题中,摆放第 index 行的皇后的位置
     *
     * @param n
     * @param index
     * @param row
     */
    private void putQueue(int n, int index, List<Integer> row) {
        if (index == n) {
            res.add(generateBoard(n, row));
            return;
        }
        // i 表示第几列,循环的过程就是在尝试给每一行的每一列放置皇后,看看在列上能不能放,看看在对角线上能不能放
        // 其实 n 皇后问题和使用回溯解决排列问题的思路是一致的:暴力遍历,使用额外数组记录状态,一层层减少,递归到底以后回溯,回溯的过程中,一层一层地恢复状态
        for (int i = 0; i < n; i++) {
            if (!col[i] && !dia1[index + i] && !dia2[index - i + n - 1]) {
                row.add(i);
                col[i] = true;
                dia1[index + i] = true;
                dia2[index - i + n - 1] = true;
                putQueue(n, index + 1, row);
                col[i] = false;
                dia1[index + i] = false;
                dia2[index - i + n - 1] = false;
                row.remove(row.size() - 1);
            }
        }
    }


    private List<String> generateBoard(int n, List<Integer> row) {
        List<String> res = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            sb.append(".");
        }
        StringBuilder cur = null;
        for (int i = 0; i < n; i++) {
            cur = new StringBuilder(sb);
            int queueLoc = row.get(i);
            cur.replace(queueLoc, queueLoc + 1, "Q");
            res.add(cur.toString());
        }
        return res;
    }

    // 测试用例
    public static void main(String[] args) {
        Solution solution = new Solution();
        List<List<String>> solveNQueens = solution.solveNQueens(8);
        for (int i = 0; i < solveNQueens.size(); i++) {
            System.out.println("第 " + (i + 1) + " 种解法:");
            List<String> sloveOne = solveNQueens.get(i);
            printList(sloveOne);
            System.out.println("********");
        }
    }

    private static void printList(List<String> sloveOne) {
        for (int i = 0; i < sloveOne.size(); i++) {
            System.out.println(sloveOne.get(i));

        }
    }
}

Knowledge supplement: n queens problem has many optimization ideas, you can speed up the search process. (Because of the time, then we'll focus)

Exercise

Exercise 1: LeetCode question 52: N-Queens II

Portal: English Website: 52. The N-Queens II , Chinese Web site: 52. The N Queen II .

n research queens problem is how to n queens placed n × n on the board, and the queen can not attack each other to each other.

414598-61e24b213783f952.png
LeetCode question 52: N-Queens II

A Method for Solving the picture shows the 8 queens problem.

Given an integer n , returns the n number of different solutions of the queen.

Example:

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

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

Java code:

import java.util.Stack;

public class Solution {

    private boolean[] marked;
    private int count;

    public int totalNQueens(int n) {
        if (n == 0 || n == 1) {
            return n;
        }
        int[] board = new int[n];
        for (int i = 0; i < n; i++) {
            board[i] = i;
        }
        permuta(board);
        return count;
    }

    // 生成一个 [0,1,...,n-1] 的全排列
    private void permuta(int[] board) {
        int len = board.length;
        marked = new boolean[len];
        Stack<Integer> pre = new Stack<>();
        findPermutation(board, 0, len, pre);
    }

    private void findPermutation(int[] board, int usedCount, int len, Stack<Integer> pre) {
        if (usedCount == len) {
            // 判断是否是符合要求的棋盘布局
            if (noDanger(pre, len)) {
                count++;
            }
            return;
        }

        for (int i = 0; i < len; i++) {
            if (!marked[i]) {
                marked[i] = true;
                pre.push(board[i]);
                findPermutation(board, usedCount + 1, len, pre);
                marked[i] = false;
                pre.pop();
            }

        }
    }

    private boolean noDanger(Stack<Integer> pre, int len) {
        int[] board = new int[len];
        for (int i = 0; i < len; i++) {
            board[i] = pre.get(i);
        }
        for (int i = 0; i < len - 1; i++) {
            for (int j = i + 1; j < len; j++) {
                // 得到所有不同的 i j 的组合,是一个组合问题,按顺序来就行
                // System.out.println(i + "\t" + j);
                if (i - j == board[i] - board[j]) {
                    return false;
                }
                if (i - j == -(board[i] - board[j])) {
                    return false;
                }
            }

        }
        // 走到这里表示通过检验
        // System.out.println(Arrays.toString(board));
        return true;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int totalNQueens = solution.totalNQueens(8);
        System.out.println(totalNQueens);
    }
}

Exercise 2: LeetCode 37th title: Solving Sudoku

Portal: 37. stops alone .

Analysis: This is even cooler than the n-queens problem issues, typical problems of artificial intelligence automatically be resolved, plus a recursive backtracking, effective pruning, beginning chapters of artificial intelligence in general will search problems.

Write a program to solve Sudoku problems through the filled spaces.

Sudoku to be a solution to the following rules :

  1. Numbers 1-9appear only once in each row.
  2. Numbers 1-9appear only once in each column.
  3. Digital 1-9each separated by a thick solid line 3x3can appear only once in utero.

Blank grid with '.'representation.

414598-1dfce7ac9fc8384d.png
LeetCode 37th title: Solving Sudoku -1

A Sudoku.
414598-4ecee8f489452404.png
LeetCode 37th title: Solving Sudoku -2

The answer is marked in red.

Note:

  • Given Sudoku sequences contain only numbers 1-9and characters '.'.
  • You may assume that a given Sudoku has only one solution.
  • Sudoku will always be given 9x9form.

Python code:

import time
import sys


# 虽然成功了,但是已经超时!!!

# 37. 解数独
# 编写一个程序,通过已填充的空格来解决数独问题。
#
# 一个数独的解法需遵循如下规则:
#
# 数字 1-9 在每一行只能出现一次。
# 数字 1-9 在每一列只能出现一次。
# 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
# 空白格用 '.' 表示。
class Solution:

    def __print(self, board):
        result = ''
        # i 表示行,是纵坐标,要特别注意
        for i in range(9):
            # j 表示列,是横坐标,要特别注意
            for j in range(9):
                if board[i][j] == '.':
                    result += ' '
                else:
                    result += board[i][j]
                result += ' '
            result += '\n'
        return result

    def check(self, board, x, y):
        # x 表示横坐标,y 表示纵坐标
        num = board[x][y]
        # 水平方向上,已经出现的数
        h_nums = [board[x][col_index] for col_index in range(9) if col_index != y and board[x][col_index] != '.']
        # 判断
        if num in h_nums:
            return False
        # print('h_nums', h_nums)

        # 垂直方向方向上,已经出现的数
        v_nums = [board[row_index][y] for row_index in range(9) if row_index != x and board[row_index][y] != '.']
        # 判断
        if num in v_nums:
            return False
        # print('v_nums', v_nums)

        # 重点理解下面这个变量的定义:所在小正方形左上角的横坐标
        x_left = (x // 3) * 3
        # 重点理解下面这个变量的定义:所在小正方形左上角的纵坐标
        y_up = (y // 3) * 3

        block_nums = []
        for row in range(3):
            for col in range(3):
                if not ((x_left + row) == x and (y_up + col) == y):
                    if board[x_left + row][y_up + col] != 0:
                        block_nums.append(board[x_left + row][y_up + col])

        # print('block_nums', block_nums)

        if num in block_nums:
            return False
        # 以上 3 个条件都判断完以后,才能把 val 放在坐标 (x,y) 处
        return True

    def next(self, board):
        # i 表示每一行
        for i in range(9):
            # j 表示每一列
            for j in range(9):
                if board[i][j] == '.':
                    return i, j
        # 表示没有下一个元素了,数独任务完成
        return False

    def __accept(self, board):
        # 如果没有了,就表示填完,返回 True
        if self.next(board) is False:
            return True
        # 否则就表示数独任务没有完成
        return False

    def __solve(self, board):
        # time.sleep(0.1)
        # print(board)
        # sys.stdout.flush()

        # 先写递归终止条件
        if self.__accept(board):
            return True
        x, y = self.next(board)
        for i in range(1, 10):
            board[x][y] = str(i)
            if self.check(board, x, y) and self.__solve(board):
                return True
            board[x][y] = '.'
        return False

    def solveSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: void Do not return anything, modify board in-place instead.
        """
        self.__solve(board)


if __name__ == '__main__':
    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']]
    solution = Solution()
    solution.solveSudoku(board)

    for row in board:
        print(row)

This part of the contents of this point, the next part of our study of dynamic programming.

(End of this section)

Guess you like

Origin blog.csdn.net/weixin_34228662/article/details/90885293