【leetcode-回溯】N皇后/金字塔转换矩阵

N皇后1

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

在这里插入图片描述

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

示例 2:
输入:n = 1
输出:[[“Q”]]

回溯

class Solution {
    public List<List<String>> solveNQueens(int n) {
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(board[i], '.');
        List<List<String>> result = new ArrayList<>();
        backtrack(result, board, 0, n);
        return result;
    }

    private void backtrack(List<List<String>> result, char[][] board, int row, int n) {
        if (row == n) {
            List<String> ans = new ArrayList<>();
            for (int i = 0; i < n; i++)
                ans.add(new String(board[i]));
            result.add(ans);
            return;
        }

        for (int j = 0; j < n; j++) {
            if (valid(board, row, j, n)) {
                board[row][j] = 'Q';
                backtrack(result, board, row + 1, n);
                board[row][j] = '.';
            }
        }
    }

    private boolean valid(char[][] board, int row, int col, int n) {
        for (int i = 0; i < row; i++) {
            if (board[i][col] == 'Q')
                return false;
        }
        for (int j = 0; j < col; j++) {
            if (board[row][j] == 'Q')
                return false;
        }
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (board[i][j] == 'Q')
                return false;
        }
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] == 'Q')
                return false;
        }
        return true;
    }
}

回溯2

用数组存储状态,可以减少遍历。

class Solution {
    private boolean[] columns;
    private boolean[] diagonal1;
    private boolean[] diagonal2;
    public List<List<String>> solveNQueens(int n) {
        columns = new boolean[n];
        diagonal1 = new boolean[2 * n - 1];
        diagonal2 = new boolean[2 * n - 1];
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(board[i], '.');

        List<List<String>> result = new ArrayList<>();
        backtrack(result, board, 0, n);
        return result;
    }

    private void backtrack(List<List<String>> result, char[][] board, int r, int n) {
        if (r == n) {
            List<String> ans = new ArrayList<>();
            for (int i = 0; i < n; i++)
                ans.add(new String(board[i]));
            result.add(ans);
            return;
        }

        for (int j = 0; j < n; j++) {
            if (valid(r, j, n)) {
                setValid(r, j, n);
                board[r][j] = 'Q';
                backtrack(result, board, r + 1, n);
                board[r][j] = '.';
                setValid(r, j, n);
            }
        }
    }

    private void setValid(int r, int c, int n) {
        columns[c] = !columns[c];
        diagonal1[r + c] = !diagonal1[r + c];
        diagonal2[r -c + n - 1] = !diagonal2[r - c + n - 1];
    }

    private boolean valid(int r, int c, int n) {
        return !(columns[c] | diagonal1[r + c] | diagonal2[r - c + n - 1]);
    }
}

N皇后2

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

示例 1:
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:
输入:n = 1
输出:1

回溯

class Solution {
    private boolean[] columns;
    private boolean[] diagonal1;
    private boolean[] diagonal2;
    private int count = 0;
    public int totalNQueens(int n) {
        columns = new boolean[n];
        diagonal1 = new boolean[2 * n - 1];
        diagonal2 = new boolean[2 * n - 1];
        char[][] board = new char[n][n];
        for (int i = 0; i < n; i++)
            Arrays.fill(board[i], '.');

        backtrack(board, 0, n);
        return count;
    }

    private void backtrack(char[][] board, int r, int n) {
        if (r == n) {
            count++;
            return;
        }

        for (int j = 0; j < n; j++) {
            if (valid(r, j, n)) {
                setValid(r, j, n);
                board[r][j] = 'Q';
                backtrack(board, r + 1, n);
                board[r][j] = '.';
                setValid(r, j, n);
            }
        }
    }

    private void setValid(int r, int c, int n) {
        columns[c] = !columns[c];
        diagonal1[r + c] = !diagonal1[r + c];
        diagonal2[r -c + n - 1] = !diagonal2[r - c + n - 1];
    }

    private boolean valid(int r, int c, int n) {
        return !(columns[c] | diagonal1[r + c] | diagonal2[r - c + n - 1]);
    }
}

基于位运算的回溯

class Solution {
    public int totalNQueens(int n) {
        return solve(n, 0, 0, 0, 0);
    }

    public int solve(int n, int row, int columns, int diagonals1, int diagonals2) {
        if (row == n) {
            return 1;
        } else {
            int count = 0;
            int availablePositions = ((1 << n) - 1) & (~(columns | diagonals1 | diagonals2));
            while (availablePositions != 0) {
                int position = availablePositions & (-availablePositions);
                availablePositions = availablePositions & (availablePositions - 1);
                count += solve(n, row + 1, columns | position, (diagonals1 | position) << 1, (diagonals2 | position) >> 1);
            }
            return count;
        }
    }
}

金字塔转换矩阵

现在,我们用一些方块来堆砌一个金字塔。 每个方块用仅包含一个字母的字符串表示。
使用三元组表示金字塔的堆砌规则如下:

  • 对于三元组 ABC ,C 为顶层方块,方块 A 、B 分别作为方块 C 下一层的的左、右子块。当且仅当 ABC 是被允许的三元组,我们才可以将其堆砌上。
  • 初始时,给定金字塔的基层 bottom,用一个字符串表示。一个允许的三元组列表 allowed,每个三元组用一个长度为 3 的字符串表示。
  • 如果可以由基层一直堆到塔尖就返回 true ,否则返回 false 。

示例 1:
输入:bottom = “BCD”, allowed = [“BCG”, “CDE”, “GEA”, “FFF”]
输出:true
解释:
可以堆砌成这样的金字塔:
在这里插入图片描述
因为符合 BCG、CDE 和 GEA 三种规则。

示例 2:
输入:bottom = “AABA”, allowed = [“AAA”, “AAB”, “ABA”, “ABB”, “BAC”]
输出:false
解释:
无法一直堆到塔尖。
注意, 允许存在像 ABC 和 ABD 这样的三元组,其中 C != D。

回溯+哈希表

class Solution {
    private Map<String, List<Character>> map;
    public boolean pyramidTransition(String bottom, List<String> allowed) {
        map = new HashMap<>(); 
        for (String s : allowed) {
            String key = s.substring(0, 2);
            List<Character> list = map.getOrDefault(key, new ArrayList<Character>());
            list.add(s.charAt(2));
            map.put(key, list);
        }
        return dfs(bottom, new StringBuilder(), 0);
    }

    private boolean dfs(String bottom, StringBuilder next, int start) {
        if (bottom.length() == 1)
            return true;

        if (next.length() + 1 == bottom.length())
            return dfs(next.toString(), new StringBuilder(), 0);

        String key = bottom.substring(start, start + 2);
        for (char c : map.getOrDefault(key, new ArrayList<Character>())) {
            next.append(c);
            if (dfs(bottom, next, start + 1))
                return true;
            next.deleteCharAt(next.length() - 1);
        }
        return false;
    }
}

回溯+位运算存储

class Solution {
    private int[][] map = new int[7][7];
    public boolean pyramidTransition(String bottom, List<String> allowed) {
        for (String s : allowed) {
            int a = s.charAt(0) - 'A', b = s.charAt(1) - 'A', c = s.charAt(2) - 'A';
            map[a][b] |= (1 << c);
        }

        List<Integer> nums = new ArrayList<>();
        for (int i = 0; i < bottom.length(); i++)
            nums.add(bottom.charAt(i) - 'A');
        return dfs(nums, new ArrayList<>(), 0);
    }

    private boolean dfs(List<Integer> bottom, List<Integer> next, int start) {
        if (bottom.size() == 1)
            return true;

        if (next.size() + 1 == bottom.size())
            return dfs(next, new ArrayList<>(), 0);

        int a = bottom.get(start), b = bottom.get(start + 1), c = map[a][b];
        for (int i = 0; i < 7; i++) {
            if ((1 << i & c) == 0)
                continue;
            next.add(i);
            if (dfs(bottom, next, start + 1))
                return true;
            next.remove(next.size() - 1);
        }
        return false;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值