LeetCode 51,52 N皇后问题

问题

N皇后问题是典型的回溯法问题,大学算法课上一般都会讲。问题大致意思如下,国际象棋中有一个棋子叫皇后,皇后可以攻击和她同处一行、一列、对角线的其它棋子,加入给定一个棋盘N*N,如何摆放N个皇后使得她们之间不能相互攻击。
LeetCode 51需要返回所有可能的棋子摆放,而52只需要返回有多少种可能即可。
在这里插入图片描述

解析

大框架知道了,前面的文章已经介绍了回溯法的基本框架:

 public void backtrack(List<Integer> list, int i, int n){
 	if(i==n){
 		//满足条件,是一种可能的情况,添加到集合中
 	}
 	for(int i=0;i<n;i++){
 		//剪枝,考虑满足要求的下一次回溯
 		//回溯
 		backtrack(list,i+1,n);
 		//返回到上一步
 	}
 }

只是每次的剪枝不一样,这里该如何剪枝呢?首先,列不能重复,那么我们可以创建一个set判断是否有重复的列。这里的关键是如何判断对角线。
注意到对角线都是经过当前位置且斜率为+1或者-1的直线,那么只要确定截距即可确定这条直线。 因此我们新建两个set,保存两个对角线的截距即可确定是否会产生冲突。

代码

LeetCode 51:

class Solution {
    Set<Integer> dia1;//记录对角线
    Set<Integer> dia2;//记录第二个对角线
    Set<Integer> col;//记录列
    List<List<Integer>> ans;//返回棋子处在列号的所有可能
    List<List<String>> reuslt;//所有符合要求的结果
    public List<List<String>> solveNQueens(int n) {
        dia1 = new HashSet();
        dia2 = new HashSet();
        col = new HashSet();
        ans = new ArrayList();
        backtrack(new ArrayList(),0,n);
        reuslt = construtBoard(ans,n);
        return reuslt;
    }
    //回溯法,获得列号
    public void backtrack(List<Integer> arr, int i,int n){
        if(i==n){
            ans.add(new ArrayList(arr));
        }
        for(int j=0;j<n;j++){
            if(col.contains(j)){
                continue;
            }
            //将棋盘看成成坐标系,那么对角线均为斜率为+1或者-1,只是截距不同,因此我们每次保存正负对角线的截距即可
            int diac1 = i - j;
            int diac2 = i + j;
            if(dia1.contains(diac1)||dia2.contains(diac2)){
                continue;
            }
            arr.add(j);
            col.add(j);
            dia1.add(diac1);
            dia2.add(diac2);
            backtrack(arr, i+1, n);
            col.remove(j);
            dia1.remove(diac1);
            dia2.remove(diac2);
            arr.remove(arr.size()-1);
        }
    }
    //根据列号构造棋盘
    public List<List<String>> construtBoard(List<List<Integer>> ans, int n){
        List<List<String>> temp = new ArrayList();
        for(int i=0;i<ans.size();i++){
            List<String> te1 = new ArrayList();
            for(int j=0;j<n;j++){
                char ch[] = new char[n];
                Arrays.fill(ch, '.');
                ch[ans.get(i).get(j)] = 'Q';
                te1.add(String.valueOf(ch));
            }
            temp.add(te1);
        }
        return temp;
    }
}

类似的,可以得到52题的代码:
LeetCode 52:

class Solution {
    int ans = 0;
    Set<Integer> dia1;
    Set<Integer> dia2;
    Set<Integer> col;
    public int totalNQueens(int n) {
        dia1 = new HashSet();
        dia2 = new HashSet();
        col = new HashSet();
        backtrack(0,n);
        return ans;
    }
    public void backtrack(int i,int n){
        if(i==n){
            ans++;
        }
        for(int j=0;j<n;j++){
            if(col.contains(j)){
                continue;
            }
            int diac1 = i - j;
            int diac2 = i + j;
            if(dia1.contains(diac1)||dia2.contains(diac2)){
                continue;
            }
            col.add(j);
            dia1.add(diac1);
            dia2.add(diac2);
            backtrack(i+1, n);
            col.remove(j);
            dia1.remove(diac1);
            dia2.remove(diac2);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值