回溯法解n皇后问题(Java 实现)

回溯法解n皇后问题

n皇后问题描述是:将n个皇后放在n*n的棋盘上,使得任意两个皇后不在同一行,同一列和同一斜线上,找出解的总数,并输出每个解。(.表示不放皇后,Q表示放置皇后)

我的思路:根据题意可以明显得出,每行/每列有且仅有一个皇后,因此用了point数组的索引表示每个皇后的x坐标,值表示y坐标,并对y坐标进行全排列(x和y均初始化为0-n),对每个排列进行判断是否这个解不在同一斜线即可

Java 代码:

package 回溯;

import java.util.*;

/*
4

2

8

92
 */

/**
 * 思路:
 * 可以确定每行每列都有且仅有一个皇后,因此行不变,对列进行全排列,并判断各点存在两点是否在一个斜线上
 * 判断是否在斜线上:x1 - y1 与 x2 - y2 不同且 x1 + y1 与 x2 + y2 不同,这里用集合处理
 */
public class n后问题 {
    public static void main(String[] args) {
        new n后问题().solve();
    }

    // point 索引表示横坐标,值表示纵坐标
    int[] point;
    char[][] A;
    int count = 0;

    void solve() {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        point = new int[n];
        A = new char[n][n];
        init(point);
        perm(point);
        System.out.println(count);
    }

    void helper(int[] a, int start, int end) {
        if (start == end) {
            if (isOk(point)) {
                count++;
                for (int i = 0; i < end; i++) {
                    for (int j = 0; j < end; j++) {
                        A[i][j] = '.';
                    }
                }
                for (int i = 0; i < end; i++) A[i][point[i]] = 'Q';
                for (int i = 0; i < end; i++) {
                    for (int j = 0; j < end; j++) {
                        System.out.print(A[i][j]);
                    }
                    System.out.println();
                }
                System.out.println();
            }
            return;
        }
        for (int i = start; i < end; i++) {
            swap(a, start, i);
            helper(a, start + 1, end);
            swap(a, start, i);
        }
    }

    // 全排列
    void perm(int[] a) {
        helper(a, 0, a.length);
    }

    void swap(int a[], int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    // 判断一个解是否满足条件:只需判断每个斜线上是否有重复即可
    boolean isOk(int[] point) {
        int n = point.length;
        HashSet<Integer> set1 = new HashSet<>();
        HashSet<Integer> set2 = new HashSet<>();
        for (int i = 0; i < n; i++) {
            if (!set1.contains(i + point[i]) && !set2.contains(i - point[i])) {
                set1.add(i + point[i]);
                set2.add(i - point[i]);
            } else return false;
        }
        return true;
    }

    // 初始化
    void init(int[] a) {
        int n = a.length;
        for (int i = 0; i < n; i++) {
            a[i] = i;
        }
    }
}

这里判断是否在同一斜线上的方法:根据一个点(x, y),在同一斜线上的点为
(x - i, y - i)
(x - i, y + i)
(x + i, y - i)
(x + i, y + i)
对于(x1, y1),被判断的点(x2, y2)与它不在同一斜线上的充要条件是:x2 - y2 不等于 x1 - y1 且 x2 + y2 不等于 x1 + y1,代码中用的集合进行判断

输入输出示例:

4
.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..

2

提供一些测试例子(仅解的总数),拿去验证:

 1       1  
 2       0  
 3       0  
 4       2  
 5       10  
 6       4  
 7       40  
 8       92  
 9       352  
 10      724  
 11      2680  
 12      14200  
 13      73712  
 14      365596  
 15      2279184  
 16      14772512  
 17      95815104  
 18      666090624  
 19      4968057848  
 20      39029188884  
 21      314666222712  
 22      2691008701644  
 23      24233937684440  
 24      227514171973736  
 25      2207893435808352  
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值